omics_coordinate/position/
interbase.rs

1//! Interbase positions.
2
3use std::num::NonZero;
4
5use crate::position::Error;
6use crate::position::Number;
7use crate::position::ParseError;
8use crate::position::Result;
9use crate::system::Interbase;
10
11mod addition;
12mod subtraction;
13
14////////////////////////////////////////////////////////////////////////////////////////
15// Assertions
16////////////////////////////////////////////////////////////////////////////////////////
17
18const _: () = {
19    // Ensure that a value is only ever as big as the internal, numerical
20    // representation.
21    assert!(size_of::<Position>() == size_of::<Number>());
22
23    /// A function to ensure that types are `Copy`.
24    const fn is_copy<T: Copy>() {}
25    is_copy::<Position>();
26};
27
28////////////////////////////////////////////////////////////////////////////////////////
29// Position
30////////////////////////////////////////////////////////////////////////////////////////
31
32/// An interbase position.
33///
34/// Interbase positions start at zero (`0`).
35pub type Position = crate::Position<Interbase>;
36
37impl Position {
38    /// Creates a new interbase position.
39    ///
40    /// # Examples
41    ///
42    /// ```
43    /// use omics_coordinate::position::interbase::Position;
44    ///
45    /// let position = Position::new(0);
46    /// assert_eq!(position.get(), 0);
47    /// ```
48    pub const fn new(value: Number) -> Self {
49        Self {
50            system: Interbase,
51            value,
52        }
53    }
54}
55////////////////////////////////////////////////////////////////////////////////////////
56// Trait implementations
57////////////////////////////////////////////////////////////////////////////////////////
58
59impl super::r#trait::Position<Interbase> for Position {}
60
61impl std::str::FromStr for Position {
62    type Err = Error;
63
64    fn from_str(s: &str) -> Result<Self> {
65        Ok(Self::new(s.parse::<Number>().map_err(|error| {
66            Error::Parse(ParseError::Int {
67                inner: error,
68                value: s.to_string(),
69            })
70        })?))
71    }
72}
73
74/// Creates implementations to convert from smaller numbers to a position.
75macro_rules! position_from_smaller_number {
76    ($from:ty) => {
77        impl From<$from> for Position {
78            fn from(value: $from) -> Self {
79                Self::new(value as Number)
80            }
81        }
82
83        impl From<NonZero<$from>> for Position {
84            fn from(value: NonZero<$from>) -> Self {
85                Self::new(value.get() as Number)
86            }
87        }
88    };
89}
90
91#[cfg(feature = "position-u64")]
92position_from_smaller_number!(u64);
93position_from_smaller_number!(u32);
94position_from_smaller_number!(u16);
95position_from_smaller_number!(u8);
96
97#[cfg(test)]
98mod tests {
99    use std::num::NonZeroU8;
100    use std::num::NonZeroU16;
101    #[cfg(feature = "position-u64")]
102    use std::num::NonZeroU32;
103
104    use crate::Position;
105    use crate::position::Number;
106    use crate::position::Result;
107    use crate::system::Interbase;
108
109    #[test]
110    fn try_from_number() {
111        let position: Position<Interbase> = 1u32.into();
112        assert_eq!(position.get(), 1);
113    }
114
115    #[test]
116    fn distance() {
117        // Zero distance between two positions.
118        let a = Position::<Interbase>::new(10);
119        let b = Position::<Interbase>::new(10);
120        assert_eq!(a.distance_unchecked(&b), 0);
121        assert_eq!(b.distance_unchecked(&a), 0);
122
123        // Non-zero distance between two Number positions.
124        let a = Position::<Interbase>::new(Number::MAX);
125        let b = Position::<Interbase>::new(0);
126        assert_eq!(a.distance_unchecked(&b), Number::MAX);
127        assert_eq!(b.distance_unchecked(&a), Number::MAX);
128    }
129
130    #[test]
131    fn parse() {
132        let zero = "0".parse::<Position<Interbase>>().unwrap();
133        assert_eq!(zero.get(), 0);
134
135        let position = "1".parse::<Position<Interbase>>().unwrap();
136        assert_eq!(position.get(), 1);
137
138        let err = "a".parse::<Position<Interbase>>().unwrap_err();
139        assert_eq!(
140            err.to_string(),
141            "parse error: invalid digit found in string: `a`"
142        );
143    }
144
145    #[test]
146    fn from_smaller_types() -> Result<()> {
147        #[cfg(feature = "position-u64")]
148        {
149            // u32
150            let position = Position::<Interbase>::from(0u32);
151            assert_eq!(position.get(), 0);
152
153            let position = Position::<Interbase>::from(NonZeroU32::new(1).unwrap());
154            assert_eq!(position.get(), 1);
155        }
156
157        // u16
158        let position = Position::<Interbase>::from(0u16);
159        assert_eq!(position.get(), 0);
160
161        let position = Position::<Interbase>::from(NonZeroU16::new(1).unwrap());
162        assert_eq!(position.get(), 1);
163
164        // u8
165        let position = Position::<Interbase>::from(0u8);
166        assert_eq!(position.get(), 0);
167
168        let position = Position::<Interbase>::from(NonZeroU8::new(1).unwrap());
169        assert_eq!(position.get(), 1);
170
171        Ok(())
172    }
173}