noodles_sam/alignment/record/
cigar.rs

1//! Alignment record CIGAR operations.
2
3pub mod op;
4
5#[doc(hidden)]
6pub mod iter;
7
8use std::io;
9
10pub use self::op::Op;
11
12/// Alignment record CIGAR operations.
13pub trait Cigar {
14    /// Returns whether there are any operations.
15    fn is_empty(&self) -> bool;
16
17    /// Returns the number of operations.
18    fn len(&self) -> usize;
19
20    /// Returns an iterator over operations.
21    fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Op>> + '_>;
22
23    /// Calculates the alignment span over the reference sequence.
24    fn alignment_span(&self) -> io::Result<usize> {
25        let mut span = 0;
26
27        for result in self.iter() {
28            let op = result?;
29
30            if op.kind().consumes_reference() {
31                span += op.len();
32            }
33        }
34
35        Ok(span)
36    }
37
38    /// Calculates the read length.
39    fn read_length(&self) -> io::Result<usize> {
40        let mut length = 0;
41
42        for result in self.iter() {
43            let op = result?;
44
45            if op.kind().consumes_read() {
46                length += op.len();
47            }
48        }
49
50        Ok(length)
51    }
52}
53
54impl<'a> IntoIterator for &'a dyn Cigar {
55    type Item = io::Result<Op>;
56    type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;
57
58    fn into_iter(self) -> Self::IntoIter {
59        self.iter()
60    }
61}
62
63impl Cigar for Box<dyn Cigar + '_> {
64    fn is_empty(&self) -> bool {
65        (**self).is_empty()
66    }
67
68    fn len(&self) -> usize {
69        (**self).len()
70    }
71
72    fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Op>> + '_> {
73        (**self).iter()
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::*;
80    use crate::alignment::record::cigar::op::Kind;
81
82    struct T(Vec<Op>);
83
84    impl Cigar for T {
85        fn is_empty(&self) -> bool {
86            self.0.is_empty()
87        }
88
89        fn len(&self) -> usize {
90            self.0.len()
91        }
92
93        fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Op>> + '_> {
94            Box::new(self.0.iter().copied().map(Ok))
95        }
96    }
97
98    #[test]
99    fn test_into_iter() -> io::Result<()> {
100        let cigar: &dyn Cigar = &T(vec![Op::new(Kind::Match, 4)]);
101
102        assert_eq!(
103            cigar.into_iter().collect::<io::Result<Vec<_>>>()?,
104            [Op::new(Kind::Match, 4)]
105        );
106
107        Ok(())
108    }
109
110    #[test]
111    fn test_alignment_span() -> io::Result<()> {
112        let cigar: &dyn Cigar = &T(vec![
113            Op::new(Kind::Match, 36),
114            Op::new(Kind::Deletion, 4),
115            Op::new(Kind::SoftClip, 8),
116        ]);
117
118        assert_eq!(cigar.alignment_span()?, 40);
119
120        Ok(())
121    }
122
123    #[test]
124    fn test_read_length() -> io::Result<()> {
125        let cigar: &dyn Cigar = &T(vec![
126            Op::new(Kind::Match, 36),
127            Op::new(Kind::Deletion, 4),
128            Op::new(Kind::SoftClip, 8),
129        ]);
130
131        assert_eq!(cigar.read_length()?, 44);
132
133        Ok(())
134    }
135}