noodles_vcf/io/writer/header/
record.rs

1mod key;
2mod value;
3
4use std::io::{self, Write};
5
6use self::key::write_key;
7use super::write_newline;
8use crate::header::{
9    FileFormat,
10    record::{
11        self,
12        value::{
13            Collection, Map,
14            map::{AlternativeAllele, Contig, Filter, Format, Info},
15        },
16    },
17};
18
19fn write_record<W, K, F>(writer: &mut W, key: K, f: F) -> io::Result<()>
20where
21    W: Write,
22    K: AsRef<str>,
23    F: Fn(&mut W) -> io::Result<()>,
24{
25    fn write_prefix<W>(writer: &mut W) -> io::Result<()>
26    where
27        W: Write,
28    {
29        const PREFIX: &[u8] = b"##";
30        writer.write_all(PREFIX)
31    }
32
33    write_prefix(writer)?;
34    write_key(writer, key)?;
35    write_separator(writer)?;
36    f(writer)?;
37    write_newline(writer)?;
38
39    Ok(())
40}
41
42pub(super) fn write_file_format<W>(writer: &mut W, file_format: FileFormat) -> io::Result<()>
43where
44    W: Write,
45{
46    write_record(writer, &record::key::FILE_FORMAT, |w| {
47        value::write_file_format(w, file_format)
48    })
49}
50
51pub(super) fn write_info<W>(writer: &mut W, id: &str, info: &Map<Info>) -> io::Result<()>
52where
53    W: Write,
54{
55    write_record(writer, &record::key::INFO, |w| {
56        value::write_map(w, id, |x| value::map::write_info(x, info))
57    })
58}
59
60pub(super) fn write_filter<W>(writer: &mut W, id: &str, filter: &Map<Filter>) -> io::Result<()>
61where
62    W: Write,
63{
64    write_record(writer, &record::key::FILTER, |w| {
65        value::write_map(w, id, |x| value::map::write_filter(x, filter))
66    })
67}
68
69pub(super) fn write_format<W>(writer: &mut W, id: &str, format: &Map<Format>) -> io::Result<()>
70where
71    W: Write,
72{
73    write_record(writer, &record::key::FORMAT, |w| {
74        value::write_map(w, id, |x| value::map::write_format(x, format))
75    })
76}
77
78pub(super) fn write_alternative_allele<W>(
79    writer: &mut W,
80    id: &str,
81    alternative_allele: &Map<AlternativeAllele>,
82) -> io::Result<()>
83where
84    W: Write,
85{
86    write_record(writer, &record::key::ALTERNATIVE_ALLELE, |w| {
87        value::write_map(w, id, |x| {
88            value::map::write_alternative_allele(x, alternative_allele)
89        })
90    })
91}
92
93pub(super) fn write_contig<W>(writer: &mut W, id: &str, contig: &Map<Contig>) -> io::Result<()>
94where
95    W: Write,
96{
97    write_record(writer, &record::key::CONTIG, |w| {
98        value::write_map(w, id, |x| value::map::write_contig(x, contig))
99    })
100}
101
102pub(super) fn write_other<W>(
103    writer: &mut W,
104    file_format: FileFormat,
105    key: &record::key::Other,
106    collection: &Collection,
107) -> io::Result<()>
108where
109    W: Write,
110{
111    const META: &str = "META";
112
113    match collection {
114        Collection::Unstructured(vs) => {
115            for v in vs {
116                write_record(writer, key, |w| value::write_string(w, file_format, v))?;
117            }
118        }
119        Collection::Structured(maps) => {
120            for (id, map) in maps {
121                write_record(writer, key, |w| {
122                    value::write_other_map(w, map.id_tag(), id, |x| {
123                        if key.as_ref() == META {
124                            value::map::write_meta(x, map)
125                        } else {
126                            value::map::write_other(x, map)
127                        }
128                    })
129                })?;
130            }
131        }
132    }
133
134    Ok(())
135}
136
137fn write_separator<W>(writer: &mut W) -> io::Result<()>
138where
139    W: Write,
140{
141    const SEPARATOR: u8 = b'=';
142    writer.write_all(&[SEPARATOR])
143}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148
149    #[test]
150    fn test_write_other() -> Result<(), Box<dyn std::error::Error>> {
151        let mut buf = Vec::new();
152
153        let file_format = FileFormat::new(4, 4);
154        let key = "comment".parse()?;
155
156        buf.clear();
157        let collection =
158            Collection::Unstructured(vec![String::from("noodles"), String::from("vcf")]);
159        write_other(&mut buf, file_format, &key, &collection)?;
160        assert_eq!(buf, b"##comment=noodles\n##comment=vcf\n");
161
162        buf.clear();
163        let collection = Collection::Structured(
164            [
165                (String::from("noodles"), Map::default()),
166                (String::from("vcf"), Map::default()),
167            ]
168            .into_iter()
169            .collect(),
170        );
171        write_other(&mut buf, file_format, &key, &collection)?;
172        assert_eq!(buf, b"##comment=<ID=noodles>\n##comment=<ID=vcf>\n");
173
174        Ok(())
175    }
176}