noodles_sam/io/writer/
record.rs

1//! SAM record writer.
2
3mod cigar;
4mod data;
5mod flags;
6mod mapping_quality;
7mod name;
8mod position;
9mod quality_scores;
10mod reference_sequence_name;
11mod sequence;
12mod template_length;
13
14use std::io::{self, Write};
15
16pub use self::cigar::write_cigar;
17use self::{
18    data::write_data,
19    flags::write_flags,
20    mapping_quality::write_mapping_quality,
21    name::write_name,
22    position::write_position,
23    quality_scores::write_quality_scores,
24    reference_sequence_name::{write_mate_reference_sequence_name, write_reference_sequence_name},
25    sequence::write_sequence,
26    template_length::write_template_length,
27};
28use crate::{alignment::Record, Header};
29
30const MISSING: u8 = b'*';
31
32pub(crate) fn write_record<W, R>(writer: &mut W, header: &Header, record: &R) -> io::Result<()>
33where
34    W: Write,
35    R: Record + ?Sized,
36{
37    const DELIMITER: &[u8] = b"\t";
38
39    write_name(writer, record.name())?;
40
41    writer.write_all(DELIMITER)?;
42    let flags = record.flags()?;
43    write_flags(writer, flags)?;
44
45    writer.write_all(DELIMITER)?;
46
47    let reference_sequence_name = record
48        .reference_sequence(header)
49        .transpose()?
50        .map(|(name, _)| name.as_ref());
51
52    write_reference_sequence_name(writer, reference_sequence_name)?;
53
54    writer.write_all(DELIMITER)?;
55    let alignment_start = record.alignment_start().transpose()?;
56    write_position(writer, alignment_start)?;
57
58    writer.write_all(DELIMITER)?;
59    let mapping_quality = record.mapping_quality().transpose()?;
60    write_mapping_quality(writer, mapping_quality)?;
61
62    let cigar = record.cigar();
63
64    writer.write_all(DELIMITER)?;
65    write_cigar(writer, &cigar)?;
66
67    writer.write_all(DELIMITER)?;
68
69    let mate_reference_sequence_name = record
70        .mate_reference_sequence(header)
71        .transpose()?
72        .map(|(name, _)| name.as_ref());
73
74    write_mate_reference_sequence_name(
75        writer,
76        reference_sequence_name,
77        mate_reference_sequence_name,
78    )?;
79
80    writer.write_all(DELIMITER)?;
81    let mate_alignment_start = record.mate_alignment_start().transpose()?;
82    write_position(writer, mate_alignment_start)?;
83
84    writer.write_all(DELIMITER)?;
85    let template_length = record.template_length()?;
86    write_template_length(writer, template_length)?;
87
88    let sequence = record.sequence();
89    let base_count = sequence.len();
90
91    writer.write_all(DELIMITER)?;
92    let read_length = cigar.read_length()?;
93    write_sequence(writer, read_length, sequence)?;
94
95    writer.write_all(DELIMITER)?;
96    write_quality_scores(writer, base_count, record.quality_scores())?;
97
98    write_data(writer, record.data())?;
99
100    writeln!(writer)?;
101
102    Ok(())
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108    use crate::alignment::RecordBuf;
109
110    #[test]
111    fn test_write_record_with_data() -> io::Result<()> {
112        use crate::alignment::{record::data::field::Tag, record_buf::data::field::Value};
113
114        let mut buf = Vec::new();
115
116        let header = Header::default();
117
118        let data = [(Tag::READ_GROUP, Value::from("rg0"))]
119            .into_iter()
120            .collect();
121        let record = RecordBuf::builder().set_data(data).build();
122
123        write_record(&mut buf, &header, &record)?;
124
125        let expected = b"*\t4\t*\t0\t255\t*\t*\t0\t0\t*\t*\tRG:Z:rg0\n";
126        assert_eq!(buf, expected);
127
128        Ok(())
129    }
130}