noodles_cram/async/io/reader/
header.rs

1//! Async CRAM header reader.
2
3pub mod container;
4mod file_id;
5mod format_version;
6mod magic_number;
7
8use noodles_sam as sam;
9use tokio::io::{self, AsyncBufRead, AsyncBufReadExt, AsyncRead, BufReader};
10
11use self::{
12    file_id::read_file_id, format_version::read_format_version, magic_number::read_magic_number,
13};
14use crate::{file_definition::Version, FileDefinition, MAGIC_NUMBER};
15
16/// A CRAM header reader.
17pub struct Reader<R> {
18    inner: R,
19}
20
21impl<R> Reader<R>
22where
23    R: AsyncRead + Unpin,
24{
25    pub(super) fn new(inner: R) -> Self {
26        Self { inner }
27    }
28
29    /// Reads the magic number.
30    pub async fn read_magic_number(&mut self) -> io::Result<[u8; MAGIC_NUMBER.len()]> {
31        read_magic_number(&mut self.inner).await
32    }
33
34    /// Reads the format version.
35    pub async fn read_format_version(&mut self) -> io::Result<Version> {
36        read_format_version(&mut self.inner).await
37    }
38
39    /// Reads the file ID.
40    pub async fn read_file_id(&mut self) -> io::Result<[u8; 20]> {
41        read_file_id(&mut self.inner).await
42    }
43
44    /// Returns a header container reader.
45    pub async fn container_reader(&mut self) -> io::Result<container::Reader<&mut R>> {
46        let len = container::read_header(&mut self.inner).await?;
47        Ok(container::Reader::new(&mut self.inner, len))
48    }
49}
50
51pub(super) async fn read_header<R>(reader: &mut R) -> io::Result<sam::Header>
52where
53    R: AsyncRead + Unpin,
54{
55    read_file_definition(reader).await?;
56    read_file_header(reader).await
57}
58
59pub(super) async fn read_file_definition<R>(reader: &mut R) -> io::Result<FileDefinition>
60where
61    R: AsyncRead + Unpin,
62{
63    let mut header_reader = Reader::new(reader);
64    read_file_definition_inner(&mut header_reader).await
65}
66
67async fn read_file_definition_inner<R>(reader: &mut Reader<R>) -> io::Result<FileDefinition>
68where
69    R: AsyncRead + Unpin,
70{
71    use crate::io::reader::header::magic_number;
72
73    reader
74        .read_magic_number()
75        .await
76        .and_then(magic_number::validate)?;
77
78    let version = reader.read_format_version().await?;
79    let file_id = reader.read_file_id().await?;
80
81    Ok(FileDefinition::new(version, file_id))
82}
83
84pub(super) async fn read_file_header<R>(reader: &mut R) -> io::Result<sam::Header>
85where
86    R: AsyncRead + Unpin,
87{
88    let mut header_reader = Reader::new(reader);
89    read_file_header_inner(&mut header_reader).await
90}
91
92async fn read_file_header_inner<R>(reader: &mut Reader<R>) -> io::Result<sam::Header>
93where
94    R: AsyncRead + Unpin,
95{
96    let mut container_reader = reader.container_reader().await?;
97
98    let header = {
99        let mut raw_sam_header_reader = container_reader.raw_sam_header_reader().await?;
100        let header = read_sam_header(&mut raw_sam_header_reader).await?;
101        raw_sam_header_reader.discard_to_end().await?;
102        header
103    };
104
105    container_reader.discard_to_end().await?;
106
107    Ok(header)
108}
109
110async fn read_sam_header<R>(reader: &mut R) -> io::Result<sam::Header>
111where
112    R: AsyncRead + Unpin,
113{
114    let mut parser = sam::header::Parser::default();
115
116    let mut header_reader = BufReader::new(reader);
117    let mut buf = Vec::new();
118
119    while read_line(&mut header_reader, &mut buf).await? != 0 {
120        parser
121            .parse_partial(&buf)
122            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
123    }
124
125    Ok(parser.finish())
126}
127
128async fn read_line<R>(reader: &mut R, dst: &mut Vec<u8>) -> io::Result<usize>
129where
130    R: AsyncBufRead + Unpin,
131{
132    const LINE_FEED: u8 = b'\n';
133    const CARRIAGE_RETURN: u8 = b'\r';
134
135    dst.clear();
136
137    match reader.read_until(LINE_FEED, dst).await? {
138        0 => Ok(0),
139        n => {
140            if dst.ends_with(&[LINE_FEED]) {
141                dst.pop();
142
143                if dst.ends_with(&[CARRIAGE_RETURN]) {
144                    dst.pop();
145                }
146            }
147
148            Ok(n)
149        }
150    }
151}