noodles_vcf/io/writer/header/
record.rs1mod 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}