noodles_fasta/record/sequence/
complement.rs

1//! FASTA record sequence base complement.
2
3use std::{error, fmt, iter::FusedIterator, slice};
4
5use bstr::ByteSlice;
6
7/// An iterator that returns the complement of a sequence.
8pub struct Complement<'a> {
9    iter: slice::Iter<'a, u8>,
10}
11
12impl<'a> Complement<'a> {
13    pub(super) fn new(iter: slice::Iter<'a, u8>) -> Self {
14        Self { iter }
15    }
16}
17
18/// An error returned when a base does not have a complement.
19#[derive(Clone, Debug, Eq, PartialEq)]
20pub struct ComplementError(u8);
21
22impl error::Error for ComplementError {}
23
24impl fmt::Display for ComplementError {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        let bytes = [self.0];
27        write!(f, "invalid base: {:?}", bytes.as_bstr())
28    }
29}
30
31impl Iterator for Complement<'_> {
32    type Item = Result<u8, ComplementError>;
33
34    fn next(&mut self) -> Option<Self::Item> {
35        self.iter.next().copied().map(complement)
36    }
37}
38
39impl DoubleEndedIterator for Complement<'_> {
40    fn next_back(&mut self) -> Option<Self::Item> {
41        self.iter.next_back().copied().map(complement)
42    }
43}
44
45impl ExactSizeIterator for Complement<'_> {}
46
47impl FusedIterator for Complement<'_> {}
48
49fn complement(b: u8) -> Result<u8, ComplementError> {
50    match b {
51        b'A' => Ok(b'T'),
52        b'C' => Ok(b'G'),
53        b'G' => Ok(b'C'),
54        b'T' => Ok(b'A'),
55        b'U' => Ok(b'A'),
56        b'W' => Ok(b'W'),
57        b'S' => Ok(b'S'),
58        b'M' => Ok(b'K'),
59        b'K' => Ok(b'M'),
60        b'R' => Ok(b'Y'),
61        b'Y' => Ok(b'R'),
62        b'B' => Ok(b'V'),
63        b'D' => Ok(b'H'),
64        b'H' => Ok(b'D'),
65        b'V' => Ok(b'B'),
66        b'N' => Ok(b'N'),
67
68        b'a' => Ok(b't'),
69        b'c' => Ok(b'g'),
70        b'g' => Ok(b'c'),
71        b't' => Ok(b'a'),
72        b'u' => Ok(b'a'),
73        b'w' => Ok(b'w'),
74        b's' => Ok(b's'),
75        b'm' => Ok(b'k'),
76        b'k' => Ok(b'm'),
77        b'r' => Ok(b'y'),
78        b'y' => Ok(b'r'),
79        b'b' => Ok(b'v'),
80        b'd' => Ok(b'h'),
81        b'h' => Ok(b'd'),
82        b'v' => Ok(b'b'),
83        b'n' => Ok(b'n'),
84
85        _ => Err(ComplementError(b)),
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_next() -> Result<(), ComplementError> {
95        let complement = Complement::new(b"ACGT".iter());
96        let actual: Vec<_> = complement.collect::<Result<_, _>>()?;
97        let expected = b"TGCA";
98        assert_eq!(actual, expected);
99        Ok(())
100    }
101
102    #[test]
103    fn test_complement() {
104        assert_eq!(complement(b'A'), Ok(b'T'));
105        assert_eq!(complement(b'C'), Ok(b'G'));
106        assert_eq!(complement(b'G'), Ok(b'C'));
107        assert_eq!(complement(b'T'), Ok(b'A'));
108        assert_eq!(complement(b'U'), Ok(b'A'));
109        assert_eq!(complement(b'W'), Ok(b'W'));
110        assert_eq!(complement(b'S'), Ok(b'S'));
111        assert_eq!(complement(b'M'), Ok(b'K'));
112        assert_eq!(complement(b'K'), Ok(b'M'));
113        assert_eq!(complement(b'R'), Ok(b'Y'));
114        assert_eq!(complement(b'Y'), Ok(b'R'));
115        assert_eq!(complement(b'B'), Ok(b'V'));
116        assert_eq!(complement(b'D'), Ok(b'H'));
117        assert_eq!(complement(b'H'), Ok(b'D'));
118        assert_eq!(complement(b'V'), Ok(b'B'));
119        assert_eq!(complement(b'N'), Ok(b'N'));
120
121        assert_eq!(complement(b'a'), Ok(b't'));
122        assert_eq!(complement(b'c'), Ok(b'g'));
123        assert_eq!(complement(b'g'), Ok(b'c'));
124        assert_eq!(complement(b't'), Ok(b'a'));
125        assert_eq!(complement(b'u'), Ok(b'a'));
126        assert_eq!(complement(b'w'), Ok(b'w'));
127        assert_eq!(complement(b's'), Ok(b's'));
128        assert_eq!(complement(b'm'), Ok(b'k'));
129        assert_eq!(complement(b'k'), Ok(b'm'));
130        assert_eq!(complement(b'r'), Ok(b'y'));
131        assert_eq!(complement(b'y'), Ok(b'r'));
132        assert_eq!(complement(b'b'), Ok(b'v'));
133        assert_eq!(complement(b'd'), Ok(b'h'));
134        assert_eq!(complement(b'h'), Ok(b'd'));
135        assert_eq!(complement(b'v'), Ok(b'b'));
136        assert_eq!(complement(b'n'), Ok(b'n'));
137
138        assert_eq!(complement(b'X'), Err(ComplementError(b'X')));
139    }
140}