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 system: Base::NAME,
77 inner: error,
78 value: s.to_string(),
79 })?)
80 }
81}
82
83impl TryFrom<Number> for Position {
84 type Error = Error;
85
86 fn try_from(value: Number) -> Result<Self> {
87 Self::try_new(value)
88 }
89}
90
91impl From<NonZero<Number>> for Position {
92 fn from(value: NonZero<Number>) -> Self {
93 Self::try_new(value.get()).unwrap()
97 }
98}
99
100macro_rules! position_from_smaller_number {
102 ($from:ty) => {
103 impl From<NonZero<$from>> for Position {
104 fn from(value: NonZero<$from>) -> Self {
105 Self::try_new(value.get() as Number).unwrap()
109 }
110 }
111
112 impl TryFrom<$from> for Position {
113 type Error = Error;
114
115 fn try_from(value: $from) -> Result<Self> {
116 Self::try_new(value as Number)
117 }
118 }
119 };
120}
121
122#[cfg(feature = "position-u64")]
123position_from_smaller_number!(u32);
124position_from_smaller_number!(u16);
125position_from_smaller_number!(u8);
126
127#[cfg(test)]
128mod tests {
129 use std::num::NonZeroU8;
130 use std::num::NonZeroU16;
131 #[cfg(feature = "position-u64")]
132 use std::num::NonZeroU32;
133
134 use crate::Position;
135 use crate::position::Error;
136 use crate::position::Number;
137 use crate::position::Result;
138 use crate::system::Base;
139
140 #[test]
141 fn from_number() {
142 let error: Result<Position<Base>> = 0u32.try_into();
143 assert_eq!(
144 error.unwrap_err(),
145 Error::IncompatibleValue {
146 system: Base::NAME,
147 value: 0,
148 }
149 );
150
151 let position: Position<Base> = 1u32.try_into().unwrap();
152 assert_eq!(position.get(), 1);
153 }
154
155 #[test]
156 fn distance() {
157 let a = Position::<Base>::try_from(10u8).unwrap();
159 let b = Position::<Base>::try_from(10u8).unwrap();
160 assert_eq!(a.distance_unchecked(&b), 0);
161 assert_eq!(b.distance_unchecked(&a), 0);
162
163 let a = Position::<Base>::try_from(Number::MAX).unwrap();
165 let b = Position::<Base>::try_from(1u8).unwrap();
166 assert_eq!(a.distance_unchecked(&b), Number::MAX - 1);
167 assert_eq!(b.distance_unchecked(&a), Number::MAX - 1);
168 }
169
170 #[test]
171 fn parse() {
172 let err = "0".parse::<Position<Base>>().unwrap_err();
173 assert_eq!(
174 err.to_string(),
175 "incompatible value for system \"base coordinate system\": `0`"
176 );
177
178 let position = "1".parse::<Position<Base>>().unwrap();
179 assert_eq!(position.get(), 1);
180
181 let err = "a".parse::<Position<Base>>().unwrap_err();
182 assert_eq!(
183 err.to_string(),
184 "parse error: failed to parse base coordinate system position from `a`: invalid digit \
185 found in string"
186 );
187 }
188
189 #[test]
190 fn from_smaller_types() -> Result<()> {
191 #[cfg(feature = "position-u64")]
192 {
193 let position = Position::<Base>::try_from(1u32)?;
195 assert_eq!(position.get(), 1);
196
197 let position = Position::<Base>::from(NonZeroU32::new(1).unwrap());
198 assert_eq!(position.get(), 1);
199 }
200
201 let position = Position::<Base>::try_from(1u16)?;
203 assert_eq!(position.get(), 1);
204
205 let position = Position::<Base>::from(NonZeroU16::new(1).unwrap());
206 assert_eq!(position.get(), 1);
207
208 let position = Position::<Base>::try_from(1u8)?;
210 assert_eq!(position.get(), 1);
211
212 let position = Position::<Base>::from(NonZeroU8::new(1).unwrap());
213 assert_eq!(position.get(), 1);
214
215 Ok(())
216 }
217}