1#[cfg(feature = "serde")]
9use serde::{Deserialize, Serialize};
10use std::fmt::{self, Display, Formatter};
11use std::ops::Neg;
12use std::str::FromStr;
13use thiserror::Error;
14
15#[derive(Debug, Clone, Copy)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18pub enum Strand {
19 Forward,
20 Reverse,
21 Unknown,
22}
23
24impl Strand {
25 pub fn from_char(strand_char: &char) -> Result<Strand, StrandError> {
33 match *strand_char {
34 '+' | 'f' | 'F' => Ok(Strand::Forward),
35 '-' | 'r' | 'R' => Ok(Strand::Reverse),
36 '.' | '?' => Ok(Strand::Unknown),
37 invalid => Err(StrandError::InvalidChar(invalid)),
38 }
39 }
40
41 pub fn strand_symbol(&self) -> &str {
45 match *self {
46 Strand::Forward => "+",
47 Strand::Reverse => "-",
48 Strand::Unknown => ".",
49 }
50 }
51
52 pub fn is_unknown(&self) -> bool {
53 matches!(*self, Strand::Unknown)
54 }
55}
56
57#[allow(clippy::match_like_matches_macro)]
58impl PartialEq for Strand {
59 fn eq(&self, other: &Strand) -> bool {
61 match (self, other) {
62 (&Strand::Forward, &Strand::Forward) => true,
63 (&Strand::Reverse, &Strand::Reverse) => true,
64 _ => false,
65 }
66 }
67}
68
69impl Neg for Strand {
70 type Output = Strand;
71 fn neg(self) -> Strand {
72 match self {
73 Strand::Forward => Strand::Reverse,
74 Strand::Reverse => Strand::Forward,
75 Strand::Unknown => Strand::Unknown,
76 }
77 }
78}
79
80#[allow(clippy::match_like_matches_macro)]
81impl Same for Strand {
82 fn same(&self, s1: &Self) -> bool {
83 match (*self, *s1) {
84 (Strand::Forward, Strand::Forward) => true,
85 (Strand::Reverse, Strand::Reverse) => true,
86 (Strand::Unknown, Strand::Unknown) => true,
87 _ => false,
88 }
89 }
90}
91
92impl Display for Strand {
93 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
94 f.write_str(self.strand_symbol())
95 }
96}
97
98impl FromStr for Strand {
99 type Err = StrandError;
100 fn from_str(s: &str) -> Result<Self, Self::Err> {
101 match s {
102 "+" | "(+)" => Ok(Strand::Forward),
103 "-" | "(-)" => Ok(Strand::Reverse),
104 "." | "" => Ok(Strand::Unknown),
105 _ => Err(StrandError::ParseError),
106 }
107 }
108}
109
110impl From<ReqStrand> for Strand {
111 fn from(rstr: ReqStrand) -> Self {
112 match rstr {
113 ReqStrand::Forward => Strand::Forward,
114 ReqStrand::Reverse => Strand::Reverse,
115 }
116 }
117}
118
119impl From<Option<ReqStrand>> for Strand {
120 fn from(orstr: Option<ReqStrand>) -> Self {
121 match orstr {
122 Some(ReqStrand::Forward) => Strand::Forward,
123 Some(ReqStrand::Reverse) => Strand::Reverse,
124 None => Strand::Unknown,
125 }
126 }
127}
128
129impl From<NoStrand> for Strand {
130 fn from(_: NoStrand) -> Self {
131 Strand::Unknown
132 }
133}
134
135#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, Copy)]
137#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
138pub enum ReqStrand {
139 Forward,
140 Reverse,
141}
142
143impl ReqStrand {
144 pub fn from_char(strand_char: &char) -> Result<ReqStrand, StrandError> {
151 match *strand_char {
152 '+' | 'f' | 'F' => Ok(ReqStrand::Forward),
153 '-' | 'r' | 'R' => Ok(ReqStrand::Reverse),
154 invalid => Err(StrandError::InvalidChar(invalid)),
155 }
156 }
157
158 pub fn strand_symbol(&self) -> &str {
161 match *self {
162 ReqStrand::Forward => "+",
163 ReqStrand::Reverse => "-",
164 }
165 }
166
167 pub fn on_strand<T>(&self, x: T) -> T
182 where
183 T: Neg<Output = T>,
184 {
185 match self {
186 ReqStrand::Forward => x,
187 ReqStrand::Reverse => -x,
188 }
189 }
190}
191
192impl Same for ReqStrand {
193 fn same(&self, s1: &Self) -> bool {
194 self == s1
195 }
196}
197
198impl Display for ReqStrand {
199 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
200 f.write_str(self.strand_symbol())
201 }
202}
203
204impl FromStr for ReqStrand {
205 type Err = StrandError;
206 fn from_str(s: &str) -> Result<Self, Self::Err> {
207 match s {
208 "+" | "(+)" => Ok(ReqStrand::Forward),
209 "-" | "(-)" => Ok(ReqStrand::Reverse),
210 _ => Err(StrandError::ParseError),
211 }
212 }
213}
214
215impl From<Strand> for Option<ReqStrand> {
216 fn from(strand: Strand) -> Option<ReqStrand> {
217 match strand {
218 Strand::Forward => Some(ReqStrand::Forward),
219 Strand::Reverse => Some(ReqStrand::Reverse),
220 Strand::Unknown => None,
221 }
222 }
223}
224
225impl From<NoStrand> for Option<ReqStrand> {
226 fn from(_: NoStrand) -> Option<ReqStrand> {
227 None
228 }
229}
230
231impl Neg for ReqStrand {
232 type Output = ReqStrand;
233 fn neg(self) -> ReqStrand {
234 match self {
235 ReqStrand::Forward => ReqStrand::Reverse,
236 ReqStrand::Reverse => ReqStrand::Forward,
237 }
238 }
239}
240
241#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, Copy)]
244#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
245pub enum NoStrand {
246 Unknown,
247}
248
249impl Neg for NoStrand {
250 type Output = NoStrand;
251 fn neg(self) -> NoStrand {
252 match self {
253 NoStrand::Unknown => NoStrand::Unknown,
254 }
255 }
256}
257
258impl Same for NoStrand {
259 fn same(&self, _s1: &Self) -> bool {
260 true
261 }
262}
263
264impl FromStr for NoStrand {
265 type Err = StrandError;
266 fn from_str(s: &str) -> Result<Self, Self::Err> {
267 match s {
268 "" => Ok(NoStrand::Unknown),
269 _ => Err(StrandError::ParseError),
270 }
271 }
272}
273
274impl Display for NoStrand {
275 fn fmt(&self, _f: &mut Formatter) -> fmt::Result {
276 Ok(())
277 }
278}
279
280pub trait Same {
284 fn same(&self, other: &Self) -> bool;
287}
288
289impl<T> Same for Option<T>
290where
291 T: Same,
292{
293 fn same(&self, s1: &Self) -> bool {
294 match (self, s1) {
295 (&Option::None, &Option::None) => true,
296 (&Option::Some(ref x), &Option::Some(ref x1)) => x.same(x1),
297 (_, _) => false,
298 }
299 }
300}
301
302#[derive(Error, Debug)]
303pub enum StrandError {
304 #[error("invalid character for strand conversion: {0:?}: can not be converted to a Strand")]
305 InvalidChar(char),
306 #[error("error parsing strand")]
307 ParseError,
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313
314 #[test]
315 fn test_strand() {
316 assert_eq!(Strand::from_char(&'+').unwrap(), Strand::Forward);
317 assert_eq!(Strand::from_char(&'-').unwrap(), Strand::Reverse);
318 assert!(Strand::from_char(&'.').unwrap().is_unknown());
319 assert!(Strand::from_char(&'o').is_err());
320 assert_eq!(Strand::Forward.strand_symbol(), "+");
321 assert_eq!(Strand::Reverse.strand_symbol(), "-");
322 assert_eq!(Strand::Unknown.strand_symbol(), ".");
323 }
324
325 #[test]
326 fn test_req_strand() {
327 assert_eq!(ReqStrand::from_char(&'+').unwrap(), ReqStrand::Forward);
328 assert_eq!(ReqStrand::from_char(&'-').unwrap(), ReqStrand::Reverse);
329 assert!(ReqStrand::from_char(&'o').is_err());
330 assert_eq!(ReqStrand::Forward.strand_symbol(), "+");
331 assert_eq!(ReqStrand::Reverse.strand_symbol(), "-");
332 }
333}