noodles_cram/io/reader/
header.rs1pub 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
17pub 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 pub fn read_magic_number(&mut self) -> io::Result<[u8; MAGIC_NUMBER.len()]> {
32 read_magic_number(&mut self.inner)
33 }
34
35 pub fn read_format_version(&mut self) -> io::Result<Version> {
37 read_format_version(&mut self.inner)
38 }
39
40 pub fn read_file_id(&mut self) -> io::Result<[u8; 20]> {
42 read_file_id(&mut self.inner)
43 }
44
45 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, 0x03, 0x00, 0x00, 0x68, 0xac, 0xf3, 0x06, 0x4d, 0xaa, 0x1e, 0x29, 0xa4, 0xa0, 0x8c, 0x56, 0xee,
165 0x91, 0x9b, 0x91, 0x04, 0x21, 0x1f, ];
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}