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}