noodles_sam/record/cigar.rs
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
use std::{fmt, io, iter};
use crate::{alignment::record::cigar::Op, io::reader::record_buf::cigar::op};
/// Raw SAM record CIGAR operations.
#[derive(Eq, PartialEq)]
pub struct Cigar<'a>(&'a [u8]);
impl<'a> Cigar<'a> {
/// Creates SAM record CIGAR operations.
///
/// # Examples
///
/// ```
/// use noodles_sam::record::Cigar;
/// let cigar = Cigar::new(b"8M13N");
/// ```
pub fn new(src: &'a [u8]) -> Self {
Self(src)
}
/// Returns whether there are any CIGAR operations.
///
/// # Examples
///
/// ```
/// use noodles_sam::record::Cigar;
///
/// let cigar = Cigar::new(b"");
/// assert!(cigar.is_empty());
///
/// let cigar = Cigar::new(b"8M13N");
/// assert!(!cigar.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
/// Returns an iterator over CIGAR operations.
///
/// # Examples
///
/// ```
/// use noodles_sam::{
/// alignment::record::cigar::{op::Kind, Op},
/// record::Cigar,
/// };
///
/// let cigar = Cigar::new(b"");
/// assert!(cigar.iter().next().is_none());
///
/// let cigar = Cigar::new(b"8M13N");
/// let mut iter = cigar.iter();
/// assert_eq!(iter.next().transpose()?, Some(Op::new(Kind::Match, 8)));
/// assert_eq!(iter.next().transpose()?, Some(Op::new(Kind::Skip, 13)));
/// assert!(iter.next().is_none());
/// # Ok::<_, Box<dyn std::error::Error>>(())
/// ```
pub fn iter(&self) -> impl Iterator<Item = Result<Op, op::ParseError>> + '_ {
use crate::io::reader::record_buf::cigar::op::parse_op;
let mut src = self.0;
iter::from_fn(move || {
if src.is_empty() {
None
} else {
Some(parse_op(&mut src))
}
})
}
}
impl<'a> fmt::Debug for Cigar<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl<'a> crate::alignment::record::Cigar for Cigar<'a> {
fn is_empty(&self) -> bool {
self.is_empty()
}
fn len(&self) -> usize {
self.as_ref()
.iter()
.filter(|&b| {
matches!(
b,
b'M' | b'I' | b'D' | b'N' | b'S' | b'H' | b'P' | b'=' | b'X'
)
})
.count()
}
fn iter(&self) -> Box<dyn Iterator<Item = io::Result<Op>> + '_> {
Box::new(self.iter().map(|result| {
result
.map(|op| Op::new(op.kind(), op.len()))
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
}))
}
}
impl<'a> AsRef<[u8]> for Cigar<'a> {
fn as_ref(&self) -> &[u8] {
self.0
}
}
impl<'a> TryFrom<Cigar<'a>> for crate::alignment::record_buf::Cigar {
type Error = io::Error;
fn try_from(Cigar(src): Cigar<'a>) -> Result<Self, Self::Error> {
use crate::io::reader::record_buf::parse_cigar;
let mut cigar = Self::default();
if !src.is_empty() {
parse_cigar(src, &mut cigar)
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
}
Ok(cigar)
}
}