noodles_cram/io/reader/
header.rs

1//! CRAM header reader.
2
3pub mod container;
4mod file_id;
5mod format_version;
6pub(crate) mod magic_number;
7
8use std::io::{self, BufRead, BufReader, Read};
9
10use noodles_sam as sam;
11
12use self::{
13    file_id::read_file_id, format_version::read_format_version, magic_number::read_magic_number,
14};
15use crate::{file_definition::Version, FileDefinition, MAGIC_NUMBER};
16
17/// A CRAM header reader.
18pub struct Reader<R> {
19    inner: R,
20}
21
22impl<R> Reader<R>
23where
24    R: Read,
25{
26    pub(super) fn new(inner: R) -> Self {
27        Self { inner }
28    }
29
30    /// Reads the magic number.
31    pub fn read_magic_number(&mut self) -> io::Result<[u8; MAGIC_NUMBER.len()]> {
32        read_magic_number(&mut self.inner)
33    }
34
35    /// Reads the format version.
36    pub fn read_format_version(&mut self) -> io::Result<Version> {
37        read_format_version(&mut self.inner)
38    }
39
40    /// Reads the file ID.
41    pub fn read_file_id(&mut self) -> io::Result<[u8; 20]> {
42        read_file_id(&mut self.inner)
43    }
44
45    /// Returns a header container reader.
46    ///
47    /// The caller is responsible of discarding any extra padding in the header container, e.g.,
48    /// using [`container::Reader::discard_to_end`].
49    pub fn container_reader(&mut self) -> io::Result<container::Reader<&mut R>> {
50        let len = container::read_header(&mut self.inner)?;
51        Ok(container::Reader::new(&mut self.inner, len))
52    }
53}
54
55pub(super) fn read_header<R>(reader: &mut R) -> io::Result<sam::Header>
56where
57    R: Read,
58{
59    read_file_definition(reader)?;
60    read_file_header(reader)
61}
62
63pub(super) fn read_file_definition<R>(reader: &mut R) -> io::Result<FileDefinition>
64where
65    R: Read,
66{
67    let mut header_reader = Reader::new(reader);
68    read_file_definition_inner(&mut header_reader)
69}
70
71fn read_file_definition_inner<R>(reader: &mut Reader<R>) -> io::Result<FileDefinition>
72where
73    R: Read,
74{
75    reader
76        .read_magic_number()
77        .and_then(magic_number::validate)?;
78
79    let version = reader.read_format_version()?;
80    let file_id = reader.read_file_id()?;
81
82    Ok(FileDefinition::new(version, file_id))
83}
84
85pub(super) fn read_file_header<R>(reader: &mut R) -> io::Result<sam::Header>
86where
87    R: Read,
88{
89    let mut header_reader = Reader::new(reader);
90    read_file_header_inner(&mut header_reader)
91}
92
93fn read_file_header_inner<R>(reader: &mut Reader<R>) -> io::Result<sam::Header>
94where
95    R: Read,
96{
97    let mut container_reader = reader.container_reader()?;
98
99    let header = {
100        let mut raw_sam_header_reader = container_reader.raw_sam_header_reader()?;
101        let header = read_sam_header(&mut raw_sam_header_reader)?;
102        raw_sam_header_reader.discard_to_end()?;
103        header
104    };
105
106    container_reader.discard_to_end()?;
107
108    Ok(header)
109}
110
111fn read_sam_header<R>(reader: &mut R) -> io::Result<sam::Header>
112where
113    R: Read,
114{
115    let mut parser = sam::header::Parser::default();
116
117    let mut header_reader = BufReader::new(reader);
118    let mut buf = Vec::new();
119
120    while read_line(&mut header_reader, &mut buf)? != 0 {
121        parser
122            .parse_partial(&buf)
123            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
124    }
125
126    Ok(parser.finish())
127}
128
129fn read_line<R>(reader: &mut R, dst: &mut Vec<u8>) -> io::Result<usize>
130where
131    R: BufRead,
132{
133    const LINE_FEED: u8 = b'\n';
134    const CARRIAGE_RETURN: u8 = b'\r';
135
136    dst.clear();
137
138    match reader.read_until(LINE_FEED, dst)? {
139        0 => Ok(0),
140        n => {
141            if dst.ends_with(&[LINE_FEED]) {
142                dst.pop();
143
144                if dst.ends_with(&[CARRIAGE_RETURN]) {
145                    dst.pop();
146                }
147            }
148
149            Ok(n)
150        }
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use super::*;
157    use crate::file_definition::Version;
158
159    #[test]
160    fn test_read_file_definition() -> Result<(), Box<dyn std::error::Error>> {
161        let src = [
162            0x43, 0x52, 0x41, 0x4d, // magic number = b"CRAM"
163            0x03, 0x00, // format version = (3, 0)
164            0x00, 0x68, 0xac, 0xf3, 0x06, 0x4d, 0xaa, 0x1e, 0x29, 0xa4, 0xa0, 0x8c, 0x56, 0xee,
165            0x91, 0x9b, 0x91, 0x04, 0x21, 0x1f, // file ID
166        ];
167
168        let mut reader = &src[..];
169        let actual = read_file_definition(&mut reader)?;
170
171        let file_id = <[u8; 20]>::try_from(&src[6..])?;
172        let expected = FileDefinition::new(Version::new(3, 0), file_id);
173
174        assert_eq!(actual, expected);
175
176        Ok(())
177    }
178}