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}