omics_coordinate/position/
base.rs1mod addition;
4mod subtraction;
5
6use std::num::NonZero;
7
8use crate::position::Error;
9use crate::position::Number;
10use crate::position::ParseError;
11use crate::position::Result;
12use crate::system::Base;
13
14const _: () = {
19 assert!(size_of::<Position>() == size_of::<Number>());
22
23 const fn is_copy<T: Copy>() {}
25 is_copy::<Position>();
26};
27
28pub type Position = crate::Position<Base>;
36
37impl Position {
38 pub const fn try_new(value: Number) -> Result<Self> {
51 if value == 0 {
52 return Err(Error::IncompatibleValue {
53 system: Base::NAME,
54 value,
55 });
56 }
57
58 Ok(Self {
59 system: Base,
60 value,
61 })
62 }
63}
64
65impl super::r#trait::Position<Base> for Position {}
70
71impl std::str::FromStr for Position {
72 type Err = Error;
73
74 fn from_str(s: &str) -> Result<Self> {
75 Self::try_new(s.parse::<Number>().map_err(|error| ParseError::Int {
76 inner: error,
77 value: s.to_string(),
78 })?)
79 }
80}
81
82impl TryFrom<Number> for Position {
83 type Error = Error;
84
85 fn try_from(value: Number) -> Result<Self> {
86 Self::try_new(value)
87 }
88}
89
90impl From<NonZero<Number>> for Position {
91 fn from(value: NonZero<Number>) -> Self {
92 Self::try_new(value.get()).unwrap()
96 }
97}
98
99macro_rules! position_from_smaller_number {
101 ($from:ty) => {
102 impl From<NonZero<$from>> for Position {
103 fn from(value: NonZero<$from>) -> Self {
104 Self::try_new(value.get() as Number).unwrap()
108 }
109 }
110
111 impl TryFrom<$from> for Position {
112 type Error = Error;
113
114 fn try_from(value: $from) -> Result<Self> {
115 Self::try_new(value as Number)
116 }
117 }
118 };
119}
120
121#[cfg(feature = "position-u64")]
122position_from_smaller_number!(u32);
123position_from_smaller_number!(u16);
124position_from_smaller_number!(u8);
125
126#[cfg(test)]
127mod tests {
128 use std::num::NonZeroU8;
129 use std::num::NonZeroU16;
130 #[cfg(feature = "position-u64")]
131 use std::num::NonZeroU32;
132
133 use crate::Position;
134 use crate::position::Error;
135 use crate::position::Number;
136 use crate::position::Result;
137 use crate::system::Base;
138
139 #[test]
140 fn from_number() {
141 let error: Result<Position<Base>> = 0u32.try_into();
142 assert_eq!(error.unwrap_err(), Error::IncompatibleValue {
143 system: Base::NAME,
144 value: 0,
145 });
146
147 let position: Position<Base> = 1u32.try_into().unwrap();
148 assert_eq!(position.get(), 1);
149 }
150
151 #[test]
152 fn distance() {
153 let a = Position::<Base>::try_from(10u8).unwrap();
155 let b = Position::<Base>::try_from(10u8).unwrap();
156 assert_eq!(a.distance_unchecked(&b), 0);
157 assert_eq!(b.distance_unchecked(&a), 0);
158
159 let a = Position::<Base>::try_from(Number::MAX).unwrap();
161 let b = Position::<Base>::try_from(1u8).unwrap();
162 assert_eq!(a.distance_unchecked(&b), Number::MAX - 1);
163 assert_eq!(b.distance_unchecked(&a), Number::MAX - 1);
164 }
165
166 #[test]
167 fn parse() {
168 let err = "0".parse::<Position<Base>>().unwrap_err();
169 assert_eq!(
170 err.to_string(),
171 "incompatible value for system \"base coordinate system\": `0`"
172 );
173
174 let position = "1".parse::<Position<Base>>().unwrap();
175 assert_eq!(position.get(), 1);
176
177 let err = "a".parse::<Position<Base>>().unwrap_err();
178 assert_eq!(
179 err.to_string(),
180 "parse error: invalid digit found in string: `a`"
181 );
182 }
183
184 #[test]
185 fn from_smaller_types() -> Result<()> {
186 #[cfg(feature = "position-u64")]
187 {
188 let position = Position::<Base>::try_from(1u32)?;
190 assert_eq!(position.get(), 1);
191
192 let position = Position::<Base>::from(NonZeroU32::new(1).unwrap());
193 assert_eq!(position.get(), 1);
194 }
195
196 let position = Position::<Base>::try_from(1u16)?;
198 assert_eq!(position.get(), 1);
199
200 let position = Position::<Base>::from(NonZeroU16::new(1).unwrap());
201 assert_eq!(position.get(), 1);
202
203 let position = Position::<Base>::try_from(1u8)?;
205 assert_eq!(position.get(), 1);
206
207 let position = Position::<Base>::from(NonZeroU8::new(1).unwrap());
208 assert_eq!(position.get(), 1);
209
210 Ok(())
211 }
212}