• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rust-bio / rust-bio-types / 14434633211

14 Apr 2025 12:04AM UTC coverage: 63.129% (-0.1%) from 63.265%
14434633211

Pull #138

github

web-flow
Merge 97154dd1c into e502e1b9b
Pull Request #138: deps: bump petgraph from 0.6.5 to 0.8.1

464 of 735 relevant lines covered (63.13%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

70.73
/src/strand.rs
1
// Copyright 2014-2016 Johannes Köster.
2
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
3
// This file may not be copied, modified, or distributed
4
// except according to those terms.
5

6
//! Data types for strand information on annotations.
7

8
#[cfg(feature = "serde")]
9
use serde::{Deserialize, Serialize};
10
use std::fmt::{self, Display, Formatter};
11
use std::ops::Neg;
12
use std::str::FromStr;
13
use thiserror::Error;
14

15
/// Strand information.
16
#[derive(Debug, Clone, Copy)]
17
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18
pub enum Strand {
19
    Forward,
20
    Reverse,
21
    Unknown,
22
}
23

24
impl Strand {
25
    /// Returns a `Strand` enum representing the given char.
26
    ///
27
    /// The mapping is as follows:
28
    ///     * '+', 'f', or 'F' becomes `Strand::Forward`
29
    ///     * '-', 'r', or 'R' becomes `Strand::Reverse`
30
    ///     * '.', '?' becomes `Strand::Unknown`
31
    ///     * Any other inputs will return an `Err(StrandError::InvalidChar)`
32
    pub fn from_char(strand_char: &char) -> Result<Strand, StrandError> {
1✔
33
        match *strand_char {
1✔
34
            '+' | 'f' | 'F' => Ok(Strand::Forward),
1✔
35
            '-' | 'r' | 'R' => Ok(Strand::Reverse),
1✔
36
            '.' | '?' => Ok(Strand::Unknown),
1✔
37
            invalid => Err(StrandError::InvalidChar(invalid)),
1✔
38
        }
39
    }
40

41
    /// Symbol denoting the strand. By convention, in BED and GFF
42
    /// files, the forward strand is `+`, the reverse strand is `-`,
43
    /// and unknown or unspecified strands are `.`.
44
    pub fn strand_symbol(&self) -> &str {
1✔
45
        match *self {
1✔
46
            Strand::Forward => "+",
1✔
47
            Strand::Reverse => "-",
1✔
48
            Strand::Unknown => ".",
1✔
49
        }
50
    }
51

52
    pub fn is_unknown(&self) -> bool {
1✔
53
        matches!(*self, Strand::Unknown)
1✔
54
    }
55
}
56

57
#[allow(clippy::match_like_matches_macro)]
58
impl PartialEq for Strand {
59
    /// Returns true if both are `Forward` or both are `Reverse`, otherwise returns false.
60
    fn eq(&self, other: &Strand) -> bool {
1✔
61
        match (self, other) {
2✔
62
            (&Strand::Forward, &Strand::Forward) => true,
63
            (&Strand::Reverse, &Strand::Reverse) => true,
64
            _ => false,
×
65
        }
66
    }
67
}
68

69
impl 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)]
81
impl Same for Strand {
82
    fn same(&self, s1: &Self) -> bool {
1✔
83
        match (*self, *s1) {
2✔
84
            (Strand::Forward, Strand::Forward) => true,
85
            (Strand::Reverse, Strand::Reverse) => true,
86
            (Strand::Unknown, Strand::Unknown) => true,
87
            _ => false,
88
        }
89
    }
90
}
91

92
impl Display for Strand {
93
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1✔
94
        f.write_str(self.strand_symbol())
1✔
95
    }
96
}
97

98
impl FromStr for Strand {
99
    type Err = StrandError;
100
    fn from_str(s: &str) -> Result<Self, Self::Err> {
1✔
101
        match s {
102
            "+" | "(+)" => Ok(Strand::Forward),
1✔
103
            "-" | "(-)" => Ok(Strand::Reverse),
1✔
104
            "." | "" => Ok(Strand::Unknown),
1✔
105
            _ => Err(StrandError::ParseError),
×
106
        }
107
    }
108
}
109

110
impl From<ReqStrand> for Strand {
111
    fn from(rstr: ReqStrand) -> Self {
1✔
112
        match rstr {
1✔
113
            ReqStrand::Forward => Strand::Forward,
1✔
114
            ReqStrand::Reverse => Strand::Reverse,
1✔
115
        }
116
    }
117
}
118

119
impl 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

129
impl From<NoStrand> for Strand {
130
    fn from(_: NoStrand) -> Self {
131
        Strand::Unknown
1✔
132
    }
133
}
134

135
/// Strand information for annotations that require a strand.
136
#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, Copy)]
137
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
138
pub enum ReqStrand {
139
    Forward,
140
    Reverse,
141
}
142

143
impl ReqStrand {
144
    /// Returns a `ReqStrand` enum representing the given char.
145
    ///
146
    /// The mapping is as follows:
147
    ///     * '+', 'f', or 'F' becomes `Strand::Forward`
148
    ///     * '-', 'r', or 'R' becomes `Strand::Reverse`
149
    ///     * Any other inputs will return an `Err(StrandError::InvalidChar)`
150
    pub fn from_char(strand_char: &char) -> Result<ReqStrand, StrandError> {
1✔
151
        match *strand_char {
1✔
152
            '+' | 'f' | 'F' => Ok(ReqStrand::Forward),
1✔
153
            '-' | 'r' | 'R' => Ok(ReqStrand::Reverse),
1✔
154
            invalid => Err(StrandError::InvalidChar(invalid)),
1✔
155
        }
156
    }
157

158
    /// Symbol denoting the strand. By convention, in BED and GFF
159
    /// files, the forward strand is `+` and the reverse strand is `-`.
160
    pub fn strand_symbol(&self) -> &str {
1✔
161
        match *self {
1✔
162
            ReqStrand::Forward => "+",
1✔
163
            ReqStrand::Reverse => "-",
1✔
164
        }
165
    }
166

167
    /// Convert the (optional) strand of some other annotation
168
    /// according to this strand. That is, reverse the strand of the
169
    /// other annotation for `ReqStrand::Reverse` and leave it
170
    /// unchanged for `ReqStrand::Forward`.
171
    ///
172
    /// # Arguments
173
    ///
174
    /// * `x` is the strand information from some other annotation.
175
    ///
176
    /// ```
177
    /// use bio_types::strand::{ReqStrand,Strand};
178
    /// assert_eq!(ReqStrand::Forward.on_strand(Strand::Reverse),
179
    ///            ReqStrand::Reverse.on_strand(Strand::Forward));
180
    /// ```
181
    pub fn on_strand<T>(&self, x: T) -> T
1✔
182
    where
183
        T: Neg<Output = T>,
184
    {
185
        match self {
1✔
186
            ReqStrand::Forward => x,
1✔
187
            ReqStrand::Reverse => -x,
1✔
188
        }
189
    }
190
}
191

192
impl Same for ReqStrand {
193
    fn same(&self, s1: &Self) -> bool {
1✔
194
        self == s1
1✔
195
    }
196
}
197

198
impl Display for ReqStrand {
199
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1✔
200
        f.write_str(self.strand_symbol())
1✔
201
    }
202
}
203

204
impl FromStr for ReqStrand {
205
    type Err = StrandError;
206
    fn from_str(s: &str) -> Result<Self, Self::Err> {
1✔
207
        match s {
208
            "+" | "(+)" => Ok(ReqStrand::Forward),
1✔
209
            "-" | "(-)" => Ok(ReqStrand::Reverse),
1✔
210
            _ => Err(StrandError::ParseError),
×
211
        }
212
    }
213
}
214

215
impl 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

225
impl From<NoStrand> for Option<ReqStrand> {
226
    fn from(_: NoStrand) -> Option<ReqStrand> {
227
        None
×
228
    }
229
}
230

231
impl Neg for ReqStrand {
232
    type Output = ReqStrand;
233
    fn neg(self) -> ReqStrand {
1✔
234
        match self {
1✔
235
            ReqStrand::Forward => ReqStrand::Reverse,
1✔
236
            ReqStrand::Reverse => ReqStrand::Forward,
1✔
237
        }
238
    }
239
}
240

241
/// Strand information for annotations that definitively have no
242
/// strand information.
243
#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, Copy)]
244
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
245
pub enum NoStrand {
246
    Unknown,
247
}
248

249
impl Neg for NoStrand {
250
    type Output = NoStrand;
251
    fn neg(self) -> NoStrand {
252
        match self {
253
            NoStrand::Unknown => NoStrand::Unknown,
254
        }
255
    }
256
}
257

258
impl Same for NoStrand {
259
    fn same(&self, _s1: &Self) -> bool {
1✔
260
        true
261
    }
262
}
263

264
impl FromStr for NoStrand {
265
    type Err = StrandError;
266
    fn from_str(s: &str) -> Result<Self, Self::Err> {
1✔
267
        match s {
268
            "" => Ok(NoStrand::Unknown),
2✔
269
            _ => Err(StrandError::ParseError),
×
270
        }
271
    }
272
}
273

274
impl Display for NoStrand {
275
    fn fmt(&self, _f: &mut Formatter) -> fmt::Result {
×
276
        Ok(())
×
277
    }
278
}
279

280
/// Equality-like operator for comparing strand information. Unknown
281
/// strands are not equal, but they are the "same" as other unknown
282
/// strands.
283
pub trait Same {
284
    /// Indicate when two strands are the "same" -- two
285
    /// unknown/unspecified strands are the "same" but are not equal.
286
    fn same(&self, other: &Self) -> bool;
287
}
288

289
impl<T> Same for Option<T>
290
where
291
    T: Same,
292
{
293
    fn same(&self, s1: &Self) -> bool {
2✔
294
        match (self, s1) {
2✔
295
            (&Option::None, &Option::None) => true,
×
296
            (&Option::Some(ref x), &Option::Some(ref x1)) => x.same(x1),
2✔
297
            (_, _) => false,
×
298
        }
299
    }
300
}
301

302
#[derive(Error, Debug)]
303
pub 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)]
311
mod 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
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc