noodles_cram/crai/async/io/
writer.rs

1use async_compression::tokio::write::GzipEncoder;
2use tokio::io::{self, AsyncWrite, AsyncWriteExt};
3
4use crate::crai::Record;
5
6/// An async CRAM index writer.
7pub struct Writer<W> {
8    inner: GzipEncoder<W>,
9}
10
11impl<W> Writer<W>
12where
13    W: AsyncWrite + Unpin,
14{
15    /// Creates an async CRAM index writer.
16    ///
17    /// # Examples
18    ///
19    /// ```
20    /// use noodles_cram::crai;
21    /// let writer = crai::r#async::io::Writer::new(Vec::new());
22    /// ```
23    pub fn new(inner: W) -> Self {
24        Self {
25            inner: GzipEncoder::new(inner),
26        }
27    }
28
29    /// Returns the underlying writer.
30    ///
31    /// # Examples
32    ///
33    /// ```
34    /// use noodles_cram::crai;
35    /// let writer = crai::r#async::io::Writer::new(Vec::new());
36    /// assert!(writer.into_inner().is_empty());
37    /// ```
38    pub fn into_inner(self) -> W {
39        self.inner.into_inner()
40    }
41
42    /// Shuts down the output stream.
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// # use std::io;
48    /// #
49    /// # #[tokio::main]
50    /// # async fn main() -> io::Result<()> {
51    /// use noodles_cram::crai;
52    /// let mut writer = crai::r#async::io::Writer::new(Vec::new());
53    /// writer.shutdown().await?;
54    /// # Ok(())
55    /// # }
56    /// ```
57    pub async fn shutdown(&mut self) -> io::Result<()> {
58        self.inner.shutdown().await
59    }
60
61    /// Writes a CRAM index.
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// # use std::io;
67    /// #
68    /// # #[tokio::main]
69    /// # async fn main() -> io::Result<()> {
70    /// use noodles_core::Position;
71    /// use noodles_cram::crai;
72    ///
73    /// let mut writer = crai::r#async::io::Writer::new(Vec::new());
74    ///
75    /// let index = vec![crai::Record::new(
76    ///     Some(0),
77    ///     Position::new(10946),
78    ///     6765,
79    ///     17711,
80    ///     233,
81    ///     317811,
82    /// )];
83    ///
84    /// writer.write_index(&index).await?;
85    /// # Ok(())
86    /// # }
87    /// ```
88    pub async fn write_index(&mut self, index: &[Record]) -> io::Result<()> {
89        write_index(&mut self.inner, index).await
90    }
91}
92
93async fn write_index<W>(writer: &mut W, index: &[Record]) -> io::Result<()>
94where
95    W: AsyncWrite + Unpin,
96{
97    for record in index {
98        write_record(writer, record).await?;
99    }
100
101    Ok(())
102}
103
104async fn write_record<W>(writer: &mut W, record: &Record) -> io::Result<()>
105where
106    W: AsyncWrite + Unpin,
107{
108    const LINE_FEED: u8 = b'\n';
109
110    writer.write_all(record.to_string().as_bytes()).await?;
111    writer.write_all(&[LINE_FEED]).await?;
112    Ok(())
113}
114
115#[cfg(test)]
116mod tests {
117    use super::*;
118
119    #[tokio::test]
120    async fn test_write_record() -> Result<(), Box<dyn std::error::Error>> {
121        use noodles_core::Position;
122
123        let mut buf = Vec::new();
124
125        let record = Record::new(Some(0), Position::new(10946), 6765, 17711, 233, 317811);
126        write_record(&mut buf, &record).await?;
127
128        let expected = b"0\t10946\t6765\t17711\t233\t317811\n";
129        assert_eq!(buf, expected);
130
131        Ok(())
132    }
133}