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
}
}