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