1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Alignment record CIGAR operations.

pub mod op;

#[doc(hidden)]
pub mod iter;

use std::io;

pub use self::op::Op;

/// Alignment record CIGAR operations.
pub trait Cigar {
    /// Returns whether there are any operations.
    fn is_empty(&self) -> bool;

    /// Returns the number of operations.
    fn len(&self) -> usize;

    /// Returns an iterator over operations.
    fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Op>> + '_>;

    /// Calculates the alignment span over the reference sequence.
    fn alignment_span(&self) -> io::Result<usize> {
        let mut span = 0;

        for result in self.iter() {
            let op = result?;

            if op.kind().consumes_reference() {
                span += op.len();
            }
        }

        Ok(span)
    }

    /// Calculates the read length.
    fn read_length(&self) -> io::Result<usize> {
        let mut length = 0;

        for result in self.iter() {
            let op = result?;

            if op.kind().consumes_read() {
                length += op.len();
            }
        }

        Ok(length)
    }
}

impl<'a> IntoIterator for &'a dyn Cigar {
    type Item = io::Result<Op>;
    type IntoIter = Box<dyn Iterator<Item = Self::Item> + 'a>;

    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

impl Cigar for Box<dyn Cigar + '_> {
    fn is_empty(&self) -> bool {
        (**self).is_empty()
    }

    fn len(&self) -> usize {
        (**self).len()
    }

    fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Op>> + '_> {
        (**self).iter()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::alignment::record::cigar::op::Kind;

    struct T(Vec<Op>);

    impl Cigar for T {
        fn is_empty(&self) -> bool {
            self.0.is_empty()
        }

        fn len(&self) -> usize {
            self.0.len()
        }

        fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Op>> + '_> {
            Box::new(self.0.iter().copied().map(Ok))
        }
    }

    #[test]
    fn test_into_iter() -> io::Result<()> {
        let cigar: &dyn Cigar = &T(vec![Op::new(Kind::Match, 4)]);

        assert_eq!(
            cigar.into_iter().collect::<io::Result<Vec<_>>>()?,
            [Op::new(Kind::Match, 4)]
        );

        Ok(())
    }

    #[test]
    fn test_alignment_span() -> io::Result<()> {
        let cigar: &dyn Cigar = &T(vec![
            Op::new(Kind::Match, 36),
            Op::new(Kind::Deletion, 4),
            Op::new(Kind::SoftClip, 8),
        ]);

        assert_eq!(cigar.alignment_span()?, 40);

        Ok(())
    }

    #[test]
    fn test_read_length() -> io::Result<()> {
        let cigar: &dyn Cigar = &T(vec![
            Op::new(Kind::Match, 36),
            Op::new(Kind::Deletion, 4),
            Op::new(Kind::SoftClip, 8),
        ]);

        assert_eq!(cigar.read_length()?, 44);

        Ok(())
    }
}