omics_coordinate/position/one/addition.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
//! Addition for 1-based positions.
use crate::CheckedAdd;
use crate::position;
use crate::position::Value;
use crate::position::one::Position;
impl CheckedAdd<usize> for Position {
type Output = Self;
fn checked_add(&self, rhs: usize) -> Option<Self::Output> {
checked_add(self.inner(), rhs)
}
}
/// Checked addition for 1-based positions.
fn checked_add(lhs: &Value, rhs: usize) -> Option<Position> {
let result = match (lhs, rhs) {
// Adding two one-based usize positions can simply be computed using the
// built-in `checked_add` method for [`usize`].
(Value::Usize(lhs), rhs) => lhs.checked_add(rhs).map(Value::Usize),
// Adding the a one-based usize position to the lower bound will end
// up as `0 + position`, which is simply `position`.
(Value::LowerBound, rhs) => Some(Value::Usize(rhs)),
};
// NOTE: at the time of writing, the only error that `Position::try_new()` is an
// [`Error::IncompatibleValueForSystem`]. In this case, if we encounter that
// error, we don't want to bubble up the error—instead, we just want to
// treat the move as if it's out of bounds.
//
// Here, we treat this situation delicately to ensure any new errors introduced
// into [`Position::try_new()`] would be handled correctly in the future. In
// short, we explicitly return [`None`] when that specific error is
// encountered. Otherwise, we unwrap.
result
.map(Position::try_new)
.transpose()
.or_else(|err| match err {
position::Error::Parse(position::ParseError::IncompatibleValue(..)) => Ok(None),
err => Err(err),
})
.unwrap()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_adds_to_a_usize_position_correctly() -> std::result::Result<(), Box<dyn std::error::Error>>
{
let position = Position::try_from(1)?;
let result = position.checked_add(2).unwrap();
assert_eq!(result.inner(), &Value::Usize(3));
assert_eq!(result.get(), Some(3));
let position = Position::try_from(usize::MAX)?;
let result = position.checked_add(1);
assert_eq!(result, None);
Ok(())
}
}