noodles_bcf/io/writer.rs
1//! BCF writer.
2
3mod builder;
4pub(crate) mod header;
5mod record;
6
7use std::io::{self, Write};
8
9use byteorder::WriteBytesExt;
10use noodles_bgzf as bgzf;
11use noodles_vcf::{self as vcf, header::StringMaps};
12
13pub use self::builder::Builder;
14use self::header::write_header;
15pub(crate) use self::record::write_record;
16use crate::Record;
17
18pub(crate) const MAJOR: u8 = 2;
19pub(crate) const MINOR: u8 = 2;
20
21/// A BCF writer.
22pub struct Writer<W> {
23 inner: W,
24 string_maps: StringMaps,
25}
26
27impl<W> Writer<W> {
28 /// Returns a reference to the underlying writer.
29 ///
30 /// # Examples
31 ///
32 /// ```
33 /// # use std::io;
34 /// use noodles_bcf as bcf;
35 /// let writer = bcf::io::Writer::from(io::sink());
36 /// let _inner = writer.get_ref();
37 /// ```
38 pub fn get_ref(&self) -> &W {
39 &self.inner
40 }
41
42 /// Returns a mutable reference to the underlying writer.
43 ///
44 /// # Examples
45 ///
46 /// ```
47 /// # use std::io;
48 /// use noodles_bcf as bcf;
49 /// let mut writer = bcf::io::Writer::from(io::sink());
50 /// let _inner = writer.get_mut();
51 /// ```
52 pub fn get_mut(&mut self) -> &mut W {
53 &mut self.inner
54 }
55
56 /// Returns the underlying writer.
57 ///
58 /// # Examples
59 ///
60 /// ```
61 /// # use std::io;
62 /// use noodles_bcf as bcf;
63 /// let mut writer = bcf::io::Writer::from(io::sink());
64 /// let _inner = writer.into_inner();
65 /// ```
66 pub fn into_inner(self) -> W {
67 self.inner
68 }
69}
70
71impl<W> Writer<W>
72where
73 W: Write,
74{
75 /// Writes a VCF header.
76 ///
77 /// # Examples
78 ///
79 /// ```
80 /// # use std::io;
81 /// use noodles_bcf as bcf;
82 /// use noodles_vcf as vcf;
83 ///
84 /// let mut writer = bcf::io::Writer::new(io::sink());
85 ///
86 /// let header = vcf::Header::default();
87 /// writer.write_header(&header)?;
88 /// # Ok::<(), io::Error>(())
89 /// ```
90 pub fn write_header(&mut self, header: &vcf::Header) -> io::Result<()> {
91 write_file_format(&mut self.inner)?;
92
93 self.string_maps = StringMaps::try_from(header)
94 .map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))?;
95
96 write_header(&mut self.inner, header)
97 }
98
99 /// Writes a record.
100 ///
101 /// # Examples
102 ///
103 /// ```
104 /// # use std::io;
105 /// use noodles_bcf as bcf;
106 /// use noodles_core::Position;
107 /// use noodles_vcf::{
108 /// self as vcf,
109 /// header::{
110 /// record::value::{map::Contig, Map},
111 /// StringMaps,
112 /// },
113 /// };
114 ///
115 /// let mut writer = bcf::io::Writer::new(io::sink());
116 ///
117 /// let mut header = vcf::Header::builder()
118 /// .add_contig("sq0", Map::<Contig>::new())
119 /// .build();
120 /// *header.string_maps_mut() = StringMaps::try_from(&header)?;
121 ///
122 /// writer.write_header(&header)?;
123 ///
124 /// let record = bcf::Record::default();
125 /// writer.write_record(&header, &record)?;
126 /// # Ok::<_, Box<dyn std::error::Error>>(())
127 /// ```
128 pub fn write_record(&mut self, header: &vcf::Header, record: &Record) -> io::Result<()> {
129 write_record(&mut self.inner, header, &self.string_maps, record)
130 }
131}
132
133impl<W> Writer<bgzf::Writer<W>>
134where
135 W: Write,
136{
137 /// Creates a BCF writer with a default compression level.
138 ///
139 /// The given stream is wrapped in a BGZF encoder.
140 ///
141 /// # Examples
142 ///
143 /// ```
144 /// # use std::io;
145 /// use noodles_bcf as bcf;
146 /// let writer = bcf::io::Writer::new(io::sink());
147 /// ```
148 pub fn new(writer: W) -> Self {
149 Self::from(bgzf::Writer::new(writer))
150 }
151
152 /// Attempts to finish the output stream.
153 ///
154 /// This is typically only manually called if the underlying stream is needed before the writer
155 /// is dropped.
156 ///
157 /// # Examples
158 ///
159 /// ```
160 /// # use std::io;
161 /// use noodles_bcf as bcf;
162 /// let mut writer = bcf::io::Writer::new(io::sink());
163 /// writer.try_finish()?;
164 /// # Ok::<(), io::Error>(())
165 /// ```
166 pub fn try_finish(&mut self) -> io::Result<()> {
167 self.inner.try_finish()
168 }
169}
170
171impl<W> From<W> for Writer<W> {
172 fn from(inner: W) -> Self {
173 Self {
174 inner,
175 string_maps: StringMaps::default(),
176 }
177 }
178}
179
180impl<W> vcf::variant::io::Write for Writer<W>
181where
182 W: Write,
183{
184 fn write_variant_header(&mut self, header: &vcf::Header) -> io::Result<()> {
185 self.write_header(header)
186 }
187
188 fn write_variant_record(
189 &mut self,
190 header: &vcf::Header,
191 record: &dyn vcf::variant::Record,
192 ) -> io::Result<()> {
193 write_record(&mut self.inner, header, &self.string_maps, record)
194 }
195}
196
197fn write_file_format<W>(writer: &mut W) -> io::Result<()>
198where
199 W: Write,
200{
201 use crate::MAGIC_NUMBER;
202
203 writer.write_all(&MAGIC_NUMBER)?;
204 writer.write_u8(MAJOR)?;
205 writer.write_u8(MINOR)?;
206
207 Ok(())
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn test_write_file_format() -> io::Result<()> {
216 let mut buf = Vec::new();
217 write_file_format(&mut buf)?;
218
219 let expected = [
220 b'B', b'C', b'F', // magic
221 0x02, // major
222 0x02, // minor
223 ];
224
225 assert_eq!(buf, expected);
226
227 Ok(())
228 }
229}