#[derive(Debug, Default, Clone, Copy)]
pub struct CigarInfo {
pub sclip: i32,
pub eclip: i32,
pub ra_len: i32,
pub qa_len: i32,
}
impl CigarInfo {
pub fn from_cigar_ops<I: IntoIterator<Item = u32>>(ops: I) -> Self {
let mut info = Self::default();
let mut first = true;
for word in ops {
let len = (word >> 4) as i32;
let code = word & 0xf;
match code {
0 | 7 | 8 => {
info.ra_len += len;
info.qa_len += len;
first = false;
}
4 | 5 => {
if first {
info.sclip += len;
} else {
info.eclip += len;
}
}
2 | 3 => info.ra_len += len,
1 => info.qa_len += len,
_ => {}
}
}
info
}
}
#[cfg(test)]
mod tests {
use super::*;
const M: u32 = 0;
const I: u32 = 1;
const D: u32 = 2;
const S: u32 = 4;
const H: u32 = 5;
const fn op(len: u32, code: u32) -> u32 {
(len << 4) | code
}
#[test]
fn match_only() {
let info = CigarInfo::from_cigar_ops([op(100, M)]);
assert_eq!(info.sclip, 0);
assert_eq!(info.eclip, 0);
assert_eq!(info.ra_len, 100);
assert_eq!(info.qa_len, 100);
}
#[test]
fn leading_soft_clip() {
let info = CigarInfo::from_cigar_ops([op(5, S), op(95, M)]);
assert_eq!(info.sclip, 5);
assert_eq!(info.eclip, 0);
assert_eq!(info.ra_len, 95);
assert_eq!(info.qa_len, 95);
}
#[test]
fn trailing_soft_clip() {
let info = CigarInfo::from_cigar_ops([op(95, M), op(5, S)]);
assert_eq!(info.sclip, 0);
assert_eq!(info.eclip, 5);
assert_eq!(info.ra_len, 95);
assert_eq!(info.qa_len, 95);
}
#[test]
fn both_clips_with_indel() {
let info = CigarInfo::from_cigar_ops([
op(3, S),
op(40, M),
op(2, I),
op(50, M),
op(5, D),
op(5, M),
op(2, H),
]);
assert_eq!(info.sclip, 3);
assert_eq!(info.eclip, 2);
assert_eq!(info.ra_len, 100); assert_eq!(info.qa_len, 97); }
#[test]
fn s_then_h_both_count_as_sclip() {
let info = CigarInfo::from_cigar_ops([op(3, S), op(2, H), op(50, M)]);
assert_eq!(info.sclip, 5);
assert_eq!(info.eclip, 0);
}
#[test]
fn accepts_a_by_value_iterator() {
let ops = [op(5, S), op(40, M), op(2, I), op(50, M), op(3, S)];
let info = CigarInfo::from_cigar_ops(ops.iter().copied());
assert_eq!(info.sclip, 5);
assert_eq!(info.eclip, 3);
assert_eq!(info.ra_len, 90); assert_eq!(info.qa_len, 92); }
}