Skip to main content

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                system: Interbase::NAME,
68                inner: error,
69                value: s.to_string(),
70            })
71        })?))
72    }
73}
74
75/// Creates implementations to convert from smaller numbers to a position.
76macro_rules! position_from_smaller_number {
77    ($from:ty) => {
78        impl From<$from> for Position {
79            fn from(value: $from) -> Self {
80                Self::new(value as Number)
81            }
82        }
83
84        impl From<NonZero<$from>> for Position {
85            fn from(value: NonZero<$from>) -> Self {
86                Self::new(value.get() as Number)
87            }
88        }
89    };
90}
91
92#[cfg(feature = "position-u64")]
93position_from_smaller_number!(u64);
94position_from_smaller_number!(u32);
95position_from_smaller_number!(u16);
96position_from_smaller_number!(u8);
97
98#[cfg(test)]
99mod tests {
100    use std::num::NonZeroU8;
101    use std::num::NonZeroU16;
102    #[cfg(feature = "position-u64")]
103    use std::num::NonZeroU32;
104
105    use crate::Position;
106    use crate::position::Number;
107    use crate::position::Result;
108    use crate::system::Interbase;
109
110    #[test]
111    fn try_from_number() {
112        let position: Position<Interbase> = 1u32.into();
113        assert_eq!(position.get(), 1);
114    }
115
116    #[test]
117    fn distance() {
118        // Zero distance between two positions.
119        let a = Position::<Interbase>::new(10);
120        let b = Position::<Interbase>::new(10);
121        assert_eq!(a.distance_unchecked(&b), 0);
122        assert_eq!(b.distance_unchecked(&a), 0);
123
124        // Non-zero distance between two Number positions.
125        let a = Position::<Interbase>::new(Number::MAX);
126        let b = Position::<Interbase>::new(0);
127        assert_eq!(a.distance_unchecked(&b), Number::MAX);
128        assert_eq!(b.distance_unchecked(&a), Number::MAX);
129    }
130
131    #[test]
132    fn parse() {
133        let zero = "0".parse::<Position<Interbase>>().unwrap();
134        assert_eq!(zero.get(), 0);
135
136        let position = "1".parse::<Position<Interbase>>().unwrap();
137        assert_eq!(position.get(), 1);
138
139        let err = "a".parse::<Position<Interbase>>().unwrap_err();
140        assert_eq!(
141            err.to_string(),
142            "parse error: failed to parse interbase coordinate system position from `a`: invalid \
143             digit found in string"
144        );
145    }
146
147    #[test]
148    fn from_smaller_types() -> Result<()> {
149        #[cfg(feature = "position-u64")]
150        {
151            // u32
152            let position = Position::<Interbase>::from(0u32);
153            assert_eq!(position.get(), 0);
154
155            let position = Position::<Interbase>::from(NonZeroU32::new(1).unwrap());
156            assert_eq!(position.get(), 1);
157        }
158
159        // u16
160        let position = Position::<Interbase>::from(0u16);
161        assert_eq!(position.get(), 0);
162
163        let position = Position::<Interbase>::from(NonZeroU16::new(1).unwrap());
164        assert_eq!(position.get(), 1);
165
166        // u8
167        let position = Position::<Interbase>::from(0u8);
168        assert_eq!(position.get(), 0);
169
170        let position = Position::<Interbase>::from(NonZeroU8::new(1).unwrap());
171        assert_eq!(position.get(), 1);
172
173        Ok(())
174    }
175}