noodles_sam/io/writer/record/
cigar.rs

1use std::io::{self, Write};
2
3use crate::{
4    alignment::record::{
5        cigar::{op::Kind, Op},
6        Cigar,
7    },
8    io::writer::num,
9};
10
11/// Writes a SAM record CIGAR string.
12///
13/// # Examples
14///
15/// ```
16/// use noodles_sam::{
17///     alignment::{
18///         record::cigar::{op::Kind, Op},
19///         record_buf::Cigar,
20///     },
21///     io::writer::record::write_cigar,
22/// };
23///
24/// let mut buf = Vec::new();
25/// let cigar = Cigar::default();
26/// write_cigar(&mut buf, &cigar)?;
27/// assert_eq!(buf, b"*");
28///
29/// let mut buf = Vec::new();
30/// let cigar: Cigar = [Op::new(Kind::Match, 4)].into_iter().collect();
31/// write_cigar(&mut buf, &cigar)?;
32/// assert_eq!(buf, b"4M");
33/// # Ok::<_, std::io::Error>(())
34/// ```
35pub fn write_cigar<W, C>(writer: &mut W, cigar: &C) -> io::Result<()>
36where
37    W: Write,
38    C: Cigar,
39{
40    use super::MISSING;
41
42    if cigar.is_empty() {
43        writer.write_all(&[MISSING])?;
44    } else {
45        for result in cigar.iter() {
46            let op = result?;
47            write_op(writer, op)?;
48        }
49    }
50
51    Ok(())
52}
53
54fn write_op<W>(writer: &mut W, op: Op) -> io::Result<()>
55where
56    W: Write,
57{
58    num::write_usize(writer, op.len())?;
59    write_kind(writer, op.kind())?;
60    Ok(())
61}
62
63fn write_kind<W>(writer: &mut W, kind: Kind) -> io::Result<()>
64where
65    W: Write,
66{
67    let c = match kind {
68        Kind::Match => b'M',
69        Kind::Insertion => b'I',
70        Kind::Deletion => b'D',
71        Kind::Skip => b'N',
72        Kind::SoftClip => b'S',
73        Kind::HardClip => b'H',
74        Kind::Pad => b'P',
75        Kind::SequenceMatch => b'=',
76        Kind::SequenceMismatch => b'X',
77    };
78
79    writer.write_all(&[c])
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use crate::alignment::record_buf::Cigar as CigarBuf;
86
87    #[test]
88    fn test_write_cigar() -> io::Result<()> {
89        fn t(buf: &mut Vec<u8>, cigar: &CigarBuf, expected: &[u8]) -> io::Result<()> {
90            buf.clear();
91            write_cigar(buf, cigar)?;
92            assert_eq!(buf, expected);
93            Ok(())
94        }
95
96        let mut buf = Vec::new();
97
98        let cigar = CigarBuf::default();
99        t(&mut buf, &cigar, b"*")?;
100
101        let cigar = [Op::new(Kind::Match, 4)].into_iter().collect();
102        t(&mut buf, &cigar, b"4M")?;
103
104        let cigar: CigarBuf = [Op::new(Kind::Match, 4), Op::new(Kind::HardClip, 2)]
105            .into_iter()
106            .collect();
107        t(&mut buf, &cigar, b"4M2H")?;
108
109        Ok(())
110    }
111
112    #[test]
113    fn test_write_op() -> io::Result<()> {
114        let mut buf = Vec::new();
115
116        buf.clear();
117        write_op(&mut buf, Op::new(Kind::Match, 1))?;
118        assert_eq!(buf, b"1M");
119
120        buf.clear();
121        write_op(&mut buf, Op::new(Kind::Match, 1 << 28))?;
122        assert_eq!(buf, b"268435456M");
123
124        Ok(())
125    }
126
127    #[test]
128    fn test_write_kind() -> io::Result<()> {
129        fn t(buf: &mut Vec<u8>, kind: Kind, expected: &[u8]) -> io::Result<()> {
130            buf.clear();
131            write_kind(buf, kind)?;
132            assert_eq!(buf, expected);
133            Ok(())
134        }
135
136        let mut buf = Vec::new();
137
138        t(&mut buf, Kind::Match, b"M")?;
139        t(&mut buf, Kind::Insertion, b"I")?;
140        t(&mut buf, Kind::Deletion, b"D")?;
141        t(&mut buf, Kind::Skip, b"N")?;
142        t(&mut buf, Kind::SoftClip, b"S")?;
143        t(&mut buf, Kind::HardClip, b"H")?;
144        t(&mut buf, Kind::Pad, b"P")?;
145        t(&mut buf, Kind::SequenceMatch, b"=")?;
146        t(&mut buf, Kind::SequenceMismatch, b"X")?;
147
148        Ok(())
149    }
150}