Skip to content

Commit

Permalink
fasta/reader: Convert interval to slice range
Browse files Browse the repository at this point in the history
Support for `(Bound<usize>, Bound<usize>)` to index a slice as added in
Rust 1.53.0. To support older versions of the compiler, this converts
the interval to a slice range instead.

Fixes #27.
  • Loading branch information
zaeleus committed Jul 9, 2021
1 parent 546bc09 commit 0968ee6
Showing 1 changed file with 45 additions and 30 deletions.
75 changes: 45 additions & 30 deletions noodles-fasta/src/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ pub use self::records::Records;
use std::{
convert::TryFrom,
io::{self, BufRead, Read, Seek, SeekFrom},
ops::{Bound, RangeBounds},
ops::{Bound, Range, RangeBounds},
};

use memchr::memchr;
Expand Down Expand Up @@ -247,7 +247,7 @@ where
let mut sequence = Vec::new();
self.read_sequence(&mut sequence)?;

let range = interval_to_range_indices(interval)?;
let range = interval_to_slice_range(interval, sequence.len())?;
Ok(Record::new(definition, sequence[range].to_vec()))
}
}
Expand Down Expand Up @@ -357,44 +357,36 @@ fn resolve_region(index: &[fai::Record], region: &Region) -> io::Result<(usize,
}

// Shifts an 1-based interval to a 0-based range for slicing.
fn interval_to_range_indices(interval: Interval) -> io::Result<(Bound<usize>, Bound<usize>)> {
fn interval_to_slice_range(interval: Interval, len: usize) -> io::Result<Range<usize>> {
let start = match interval.start_bound() {
Bound::Included(&s) => {
if s > 0 {
// SAFETY: `s` cannot be < 1
Bound::Included((s - 1) as usize)
} else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid interval start bound",
));
}
Bound::Included(&s) => usize::try_from(s)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
.and_then(|s| {
s.checked_sub(1).ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "invalid start position")
})
})?,
Bound::Excluded(&s) => {
usize::try_from(s).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?
}
Bound::Excluded(&s) => usize::try_from(s)
.map(Bound::Excluded)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?,
Bound::Unbounded => Bound::Unbounded,
Bound::Unbounded => 0,
};

let end = match interval.end_bound() {
Bound::Included(&e) => {
if e > 0 {
// SAFETY: `e` cannot be < 1
Bound::Included((e - 1) as usize)
} else {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"invalid interval end bound",
));
}
usize::try_from(e).map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?
}
Bound::Excluded(&e) => usize::try_from(e)
.map(Bound::Excluded)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?,
Bound::Unbounded => Bound::Unbounded,
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
.and_then(|e| {
e.checked_sub(1).ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "invalid end position")
})
})?,
Bound::Unbounded => len,
};

Ok((start, end))
Ok(start..end)
}

#[cfg(test)]
Expand Down Expand Up @@ -502,4 +494,27 @@ mod tests {

Ok(())
}

#[test]
fn test_interval_to_slice_range() -> io::Result<()> {
const LENGTH: usize = 4;

let interval = (Bound::Unbounded, Bound::Unbounded);
let range = interval_to_slice_range(interval, LENGTH)?;
assert_eq!(range, 0..4);

let interval = (Bound::Included(2), Bound::Unbounded);
let range = interval_to_slice_range(interval, LENGTH)?;
assert_eq!(range, 1..4);

let interval = (Bound::Unbounded, Bound::Included(3));
let range = interval_to_slice_range(interval, LENGTH)?;
assert_eq!(range, 0..3);

let interval = (Bound::Included(2), Bound::Included(3));
let range = interval_to_slice_range(interval, LENGTH)?;
assert_eq!(range, 1..3);

Ok(())
}
}

0 comments on commit 0968ee6

Please sign in to comment.