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
136
137
138
139
//! Alignment record CIGAR operations buffer.

use std::io;

use crate::alignment::record::cigar::Op;

/// An alignment record CIGAR operations buffer.
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Cigar(Vec<Op>);

impl Cigar {
    /// Calculates the alignment span over the reference sequence.
    ///
    /// This sums the lengths of the CIGAR operations that consume the reference sequence, i.e.,
    /// alignment matches (`M`), deletions from the reference (`D`), skipped reference regions
    /// (`S`), sequence matches (`=`), and sequence mismatches (`X`).
    ///
    /// # Examples
    ///
    /// ```
    /// use noodles_sam::alignment::{
    ///     record::cigar::{op::Kind, Op},
    ///     record_buf::Cigar,
    /// };
    ///
    /// let cigar: Cigar = [
    ///     Op::new(Kind::Match, 36),
    ///     Op::new(Kind::Deletion, 4),
    ///     Op::new(Kind::SoftClip, 8),
    /// ]
    /// .into_iter()
    /// .collect();
    ///
    /// assert_eq!(cigar.alignment_span(), 40);
    /// ```
    pub fn alignment_span(&self) -> usize {
        self.0
            .iter()
            .filter_map(|op| op.kind().consumes_reference().then_some(op.len()))
            .sum()
    }

    /// Calculates the read length.
    ///
    /// This sums the lengths of the CIGAR operations that consume the read, i.e., alignment
    /// matches (`M`), insertions to the reference (`I`), soft clips (`S`), sequence matches (`=`),
    /// and sequence mismatches (`X`).
    ///
    /// # Examples
    ///
    /// ```
    /// use noodles_sam::alignment::{
    ///     record::cigar::{op::Kind, Op},
    ///     record_buf::Cigar,
    /// };
    ///
    /// let cigar: Cigar = [
    ///     Op::new(Kind::Match, 36),
    ///     Op::new(Kind::Deletion, 4),
    ///     Op::new(Kind::SoftClip, 8),
    /// ]
    /// .into_iter()
    /// .collect();
    ///
    /// assert_eq!(cigar.read_length(), 44);
    /// ```
    pub fn read_length(&self) -> usize {
        self.0
            .iter()
            .filter_map(|op| op.kind().consumes_read().then_some(op.len()))
            .sum()
    }
}

impl crate::alignment::record::Cigar for Cigar {
    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))
    }
}

impl crate::alignment::record::Cigar for &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()
    }
}

impl AsRef<[Op]> for Cigar {
    fn as_ref(&self) -> &[Op] {
        &self.0
    }
}

impl AsMut<Vec<Op>> for Cigar {
    fn as_mut(&mut self) -> &mut Vec<Op> {
        &mut self.0
    }
}

impl Extend<Op> for Cigar {
    fn extend<T: IntoIterator<Item = Op>>(&mut self, iter: T) {
        self.0.extend(iter);
    }
}

impl FromIterator<Op> for Cigar {
    fn from_iter<T: IntoIterator<Item = Op>>(iter: T) -> Self {
        let mut cigar = Cigar::default();
        cigar.extend(iter);
        cigar
    }
}

impl From<Vec<Op>> for Cigar {
    fn from(ops: Vec<Op>) -> Self {
        Self(ops)
    }
}

impl From<Cigar> for Vec<Op> {
    fn from(cigar: Cigar) -> Self {
        cigar.0
    }
}