noodles_vcf/io/writer.rs
1//! VCF writer.
2
3mod builder;
4mod header;
5mod record;
6
7use std::io::{self, Write};
8
9pub use self::builder::Builder;
10use self::{header::write_header, record::write_record};
11use crate::{Header, Record};
12
13/// A VCF writer.
14///
15/// # Examples
16///
17/// ```
18/// # use std::io;
19/// use noodles_core::Position;
20/// use noodles_vcf::{
21/// self as vcf,
22/// header::{
23/// record::value::{map::Contig, Map},
24/// FileFormat,
25/// },
26/// variant::io::Write,
27/// };
28///
29/// let mut writer = vcf::io::Writer::new(Vec::new());
30///
31/// let header = vcf::Header::builder()
32/// .set_file_format(FileFormat::new(4, 5))
33/// .add_contig("sq0", Map::<Contig>::new())
34/// .build();
35///
36/// writer.write_header(&header)?;
37///
38/// let record = vcf::variant::RecordBuf::builder()
39/// .set_reference_sequence_name("sq0")
40/// .set_variant_start(Position::MIN)
41/// .set_reference_bases("A")
42/// .build();
43///
44/// writer.write_variant_record(&header, &record);
45///
46/// let expected = b"##fileformat=VCFv4.5
47/// ###contig=<ID=sq0>
48/// #CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO
49/// sq0\t1\t.\tA\t.\t.\t.\t.
50/// ";
51///
52/// assert_eq!(&writer.get_ref()[..], &expected[..]);
53/// # Ok::<_, std::io::Error>(())
54/// ```
55#[derive(Debug)]
56pub struct Writer<W> {
57 inner: W,
58}
59
60impl<W> Writer<W>
61where
62 W: Write,
63{
64 /// Creates a VCF writer.
65 ///
66 /// # Examples
67 ///
68 /// ```
69 /// use noodles_vcf as vcf;
70 /// let writer = vcf::io::Writer::new(Vec::new());
71 /// ```
72 pub fn new(inner: W) -> Self {
73 Self { inner }
74 }
75
76 /// Returns a reference to the underlying writer.
77 ///
78 /// # Examples
79 ///
80 /// ```
81 /// use noodles_vcf as vcf;
82 /// let writer = vcf::io::Writer::new(Vec::new());
83 /// assert!(writer.get_ref().is_empty());
84 /// ```
85 pub fn get_ref(&self) -> &W {
86 &self.inner
87 }
88
89 /// Returns a mutable reference to the underlying writer.
90 ///
91 /// # Examples
92 ///
93 /// ```
94 /// use noodles_vcf as vcf;
95 /// let mut writer = vcf::io::Writer::new(Vec::new());
96 /// assert!(writer.get_mut().is_empty());
97 /// ```
98 pub fn get_mut(&mut self) -> &mut W {
99 &mut self.inner
100 }
101
102 /// Unwraps and returns the underlying writer.
103 ///
104 /// # Examples
105 ///
106 /// ```
107 /// use noodles_vcf as vcf;
108 /// let writer = vcf::io::Writer::new(Vec::new());
109 /// assert!(writer.into_inner().is_empty());
110 /// ```
111 pub fn into_inner(self) -> W {
112 self.inner
113 }
114
115 /// Writes a VCF header.
116 ///
117 /// # Examples
118 ///
119 /// ```
120 /// # use std::io;
121 /// use noodles_vcf as vcf;
122 ///
123 /// let mut writer = vcf::io::Writer::new(Vec::new());
124 ///
125 /// let header = vcf::Header::default();
126 /// writer.write_header(&header)?;
127 /// # Ok::<(), io::Error>(())
128 /// ```
129 pub fn write_header(&mut self, header: &Header) -> io::Result<()> {
130 write_header(&mut self.inner, header)
131 }
132
133 /// Writes a VCF record.
134 ///
135 /// # Examples
136 ///
137 /// ```
138 /// use noodles_core::Position;
139 /// use noodles_vcf as vcf;
140 ///
141 /// let mut writer = vcf::io::Writer::new(Vec::new());
142 ///
143 /// let header = vcf::Header::default();
144 /// let record = vcf::Record::default();
145 /// writer.write_record(&header, &record)?;
146 ///
147 /// assert_eq!(writer.get_ref(), b"sq0\t1\t.\tA\t.\t.\t.\t.\n");
148 /// # Ok::<_, std::io::Error>(())
149 /// ```
150 pub fn write_record(&mut self, header: &Header, record: &Record) -> io::Result<()> {
151 write_record(&mut self.inner, header, record)
152 }
153}
154
155impl<W> crate::variant::io::Write for Writer<W>
156where
157 W: Write,
158{
159 fn write_variant_header(&mut self, header: &Header) -> io::Result<()> {
160 self.write_header(header)
161 }
162
163 fn write_variant_record(
164 &mut self,
165 header: &Header,
166 record: &dyn crate::variant::Record,
167 ) -> io::Result<()> {
168 write_record(&mut self.inner, header, record)
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use noodles_core::Position;
175
176 use super::*;
177 use crate::variant::{io::Write, RecordBuf};
178
179 #[test]
180 fn test_write_variant_record() -> io::Result<()> {
181 let header = Header::default();
182
183 let record = RecordBuf::builder()
184 .set_reference_sequence_name("sq0")
185 .set_variant_start(Position::MIN)
186 .set_reference_bases("A")
187 .build();
188
189 let mut writer = Writer::new(Vec::new());
190 writer.write_variant_record(&header, &record)?;
191
192 let expected = b"sq0\t1\t.\tA\t.\t.\t.\t.\n";
193 assert_eq!(writer.get_ref(), expected);
194
195 Ok(())
196 }
197
198 #[test]
199 fn test_write_record_with_format() -> Result<(), Box<dyn std::error::Error>> {
200 use crate::variant::{
201 record::samples::keys::key,
202 record_buf::{samples::sample::Value, Samples},
203 };
204
205 let header = Header::default();
206
207 let samples = Samples::new(
208 [
209 String::from(key::GENOTYPE),
210 String::from(key::CONDITIONAL_GENOTYPE_QUALITY),
211 ]
212 .into_iter()
213 .collect(),
214 vec![vec![
215 Some(Value::String(String::from("0|0"))),
216 Some(Value::Integer(13)),
217 ]],
218 );
219
220 let record = RecordBuf::builder()
221 .set_reference_sequence_name("sq0")
222 .set_variant_start(Position::MIN)
223 .set_reference_bases("A")
224 .set_samples(samples)
225 .build();
226
227 let mut writer = Writer::new(Vec::new());
228 writer.write_variant_record(&header, &record)?;
229
230 let expected = b"sq0\t1\t.\tA\t.\t.\t.\t.\tGT:GQ\t0|0:13\n";
231 assert_eq!(writer.get_ref(), expected);
232
233 Ok(())
234 }
235}