noodles_sam/alignment/record_buf/
cigar.rs

1//! Alignment record CIGAR operations buffer.
2
3use std::io;
4
5use crate::alignment::record::cigar::Op;
6
7/// An alignment record CIGAR operations buffer.
8#[derive(Clone, Debug, Default, Eq, PartialEq)]
9pub struct Cigar(Vec<Op>);
10
11impl Cigar {
12    /// Calculates the alignment span over the reference sequence.
13    ///
14    /// This sums the lengths of the CIGAR operations that consume the reference sequence, i.e.,
15    /// alignment matches (`M`), deletions from the reference (`D`), skipped reference regions
16    /// (`S`), sequence matches (`=`), and sequence mismatches (`X`).
17    ///
18    /// # Examples
19    ///
20    /// ```
21    /// use noodles_sam::alignment::{
22    ///     record::cigar::{op::Kind, Op},
23    ///     record_buf::Cigar,
24    /// };
25    ///
26    /// let cigar: Cigar = [
27    ///     Op::new(Kind::Match, 36),
28    ///     Op::new(Kind::Deletion, 4),
29    ///     Op::new(Kind::SoftClip, 8),
30    /// ]
31    /// .into_iter()
32    /// .collect();
33    ///
34    /// assert_eq!(cigar.alignment_span(), 40);
35    /// ```
36    pub fn alignment_span(&self) -> usize {
37        self.0
38            .iter()
39            .filter_map(|op| op.kind().consumes_reference().then_some(op.len()))
40            .sum()
41    }
42
43    /// Calculates the read length.
44    ///
45    /// This sums the lengths of the CIGAR operations that consume the read, i.e., alignment
46    /// matches (`M`), insertions to the reference (`I`), soft clips (`S`), sequence matches (`=`),
47    /// and sequence mismatches (`X`).
48    ///
49    /// # Examples
50    ///
51    /// ```
52    /// use noodles_sam::alignment::{
53    ///     record::cigar::{op::Kind, Op},
54    ///     record_buf::Cigar,
55    /// };
56    ///
57    /// let cigar: Cigar = [
58    ///     Op::new(Kind::Match, 36),
59    ///     Op::new(Kind::Deletion, 4),
60    ///     Op::new(Kind::SoftClip, 8),
61    /// ]
62    /// .into_iter()
63    /// .collect();
64    ///
65    /// assert_eq!(cigar.read_length(), 44);
66    /// ```
67    pub fn read_length(&self) -> usize {
68        self.0
69            .iter()
70            .filter_map(|op| op.kind().consumes_read().then_some(op.len()))
71            .sum()
72    }
73}
74
75impl crate::alignment::record::Cigar for Cigar {
76    fn is_empty(&self) -> bool {
77        self.0.is_empty()
78    }
79
80    fn len(&self) -> usize {
81        self.0.len()
82    }
83
84    fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Op>> + '_> {
85        Box::new(self.0.iter().copied().map(Ok))
86    }
87}
88
89impl crate::alignment::record::Cigar for &Cigar {
90    fn is_empty(&self) -> bool {
91        (*self).is_empty()
92    }
93
94    fn len(&self) -> usize {
95        (*self).len()
96    }
97
98    fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Op>> + '_> {
99        (*self).iter()
100    }
101}
102
103impl AsRef<[Op]> for Cigar {
104    fn as_ref(&self) -> &[Op] {
105        &self.0
106    }
107}
108
109impl AsMut<Vec<Op>> for Cigar {
110    fn as_mut(&mut self) -> &mut Vec<Op> {
111        &mut self.0
112    }
113}
114
115impl Extend<Op> for Cigar {
116    fn extend<T: IntoIterator<Item = Op>>(&mut self, iter: T) {
117        self.0.extend(iter);
118    }
119}
120
121impl FromIterator<Op> for Cigar {
122    fn from_iter<T: IntoIterator<Item = Op>>(iter: T) -> Self {
123        let mut cigar = Cigar::default();
124        cigar.extend(iter);
125        cigar
126    }
127}
128
129impl From<Vec<Op>> for Cigar {
130    fn from(ops: Vec<Op>) -> Self {
131        Self(ops)
132    }
133}
134
135impl From<Cigar> for Vec<Op> {
136    fn from(cigar: Cigar) -> Self {
137        cigar.0
138    }
139}