1use std::io;
4
5use crate::ReadStatus;
6
7use super::{
8 index::Index,
9 record::{Id, Record},
10 version::{Version, V3, V4},
11};
12
13mod builder;
14pub use builder::Builder;
15
16mod intersect;
17pub use intersect::Intersect;
18
19mod traits;
20pub(crate) use traits::ReaderExt;
21
22pub type ReaderV3<R> = Reader<R, V3>;
24
25pub type ReaderV4<R> = Reader<R, V4>;
27
28pub struct Reader<R, V> {
33 location: Location<V>,
34 position_reader: bgzf::Reader<R>,
35 item_reader: bgzf::Reader<R>,
36}
37
38impl<R, V> Reader<R, V>
39where
40 R: io::BufRead,
41 V: Version,
42{
43 pub fn create_record_buf(&self) -> Record<Id, V::Item> {
45 V::create_record_buf(self.index())
46 }
47
48 pub fn from_bgzf(
54 index: Index<V>,
55 position_reader: bgzf::Reader<R>,
56 item_reader: bgzf::Reader<R>,
57 ) -> Option<Self> {
58 Location::setup(index).map(|location| Self {
59 location,
60 position_reader,
61 item_reader,
62 })
63 }
64
65 pub fn index(&self) -> &Index<V> {
67 &self.location.index
68 }
69
70 pub fn index_mut(&mut self) -> &mut Index<V> {
72 &mut self.location.index
73 }
74
75 pub fn into_parts(self) -> (Index<V>, bgzf::Reader<R>, bgzf::Reader<R>) {
77 (self.location.index, self.position_reader, self.item_reader)
78 }
79
80 pub fn item_reader(&self) -> &bgzf::Reader<R> {
82 &self.item_reader
83 }
84
85 pub fn item_reader_mut(&mut self) -> &mut bgzf::Reader<R> {
87 &mut self.item_reader
88 }
89
90 pub fn position_reader(&self) -> &bgzf::Reader<R> {
92 &self.position_reader
93 }
94
95 pub fn position_reader_mut(&mut self) -> &mut bgzf::Reader<R> {
97 &mut self.position_reader
98 }
99
100 pub fn read_item(&mut self, buf: &mut V::Item) -> io::Result<ReadStatus> {
105 V::read_item(&mut self.item_reader, buf)
106 }
107
108 pub fn read_magic(&mut self) -> io::Result<()> {
112 V::read_magic(&mut self.position_reader).and_then(|_| V::read_magic(&mut self.item_reader))
113 }
114
115 pub fn read_position(&mut self) -> io::Result<Option<u32>> {
120 self.position_reader.read_position()
121 }
122
123 pub fn read_record(&mut self, record: &mut Record<Id, V::Item>) -> io::Result<ReadStatus> {
128 if !self.location.contig_is_finished() || self.location.next_contig().is_some() {
129 match (self.read_position()?, self.read_item(record.item_mut())?) {
131 (Some(pos), ReadStatus::NotDone) => {
132 *record.contig_id_mut() = self.location.contig_id;
133 *record.position_mut() = pos;
134
135 self.location.next_site_on_contig();
136
137 Ok(ReadStatus::NotDone)
138 }
139 (Some(_), ReadStatus::Done) => Err(eof_err(
140 "reached EoF in SAF position file before reaching EoF in SAF item file",
141 )),
142 (None, ReadStatus::NotDone) => Err(eof_err(
143 "reached EoF in SAF item file before reaching EoF in SAF position file",
144 )),
145 (None, ReadStatus::Done) => Err(eof_err(
146 "reached EoF in both SAF files before reaching end of index",
147 )),
148 }
149 } else {
150 let position_reader_is_done = ReadStatus::check(&mut self.position_reader)?.is_done();
152 let item_reader_is_done = ReadStatus::check(&mut self.item_reader)?.is_done();
153
154 match (position_reader_is_done, item_reader_is_done) {
155 (true, true) => Ok(ReadStatus::Done),
156 (true, false) => Err(data_err(
157 "reached end of index before reaching EoF in SAF position file",
158 )),
159 (false, true) => Err(data_err(
160 "reached end of index before reaching EoF in SAF item file",
161 )),
162 (false, false) => Err(data_err(
163 "reached end of index before reaching EoF in both SAF files",
164 )),
165 }
166 }
167 }
168}
169
170impl<R, V> Reader<R, V>
171where
172 R: io::BufRead + io::Seek,
173 V: Version,
174{
175 pub fn intersect(self, other: Self) -> Intersect<R, V> {
181 Intersect::from_reader(self).intersect(other)
182 }
183
184 pub fn seek(&mut self, contig_id: usize) -> io::Result<()> {
192 self.location
193 .set_contig(contig_id)
194 .expect("cannot seek to contig ID");
195
196 let record = &self.index().records()[contig_id];
197 let position_offset = record.position_offset();
198 let item_offset = record.item_offset();
199
200 let position_vpos = bgzf::VirtualPosition::from(position_offset);
201 self.position_reader.seek(position_vpos)?;
202
203 let item_vpos = bgzf::VirtualPosition::from(item_offset);
204 self.item_reader.seek(item_vpos)?;
205
206 Ok(())
207 }
208
209 pub fn seek_by_name(&mut self, name: &str) -> io::Result<()> {
219 let contig_id = self
220 .index()
221 .records()
222 .iter()
223 .position(|x| x.name() == name)
224 .expect("name not found in index");
225
226 self.seek(contig_id)
227 }
228}
229
230#[derive(Clone, Debug, Eq, PartialEq)]
235struct Location<V> {
236 pub index: Index<V>,
237 pub contig_id: usize,
238 pub sites_left_on_contig: usize,
239}
240
241impl<V> Location<V>
242where
243 V: Version,
244{
245 pub fn contig_is_finished(&self) -> bool {
247 0 == self.sites_left_on_contig
248 }
249
250 pub fn next_site_on_contig(&mut self) {
252 self.sites_left_on_contig -= 1
253 }
254
255 pub fn next_contig(&mut self) -> Option<()> {
259 self.set_contig(self.contig_id + 1)
260 }
261
262 pub fn set_contig(&mut self, contig_id: usize) -> Option<()> {
266 self.contig_id = contig_id;
267 self.sites_left_on_contig = self.index.records().get(self.contig_id)?.sites();
268 Some(())
269 }
270
271 pub fn setup(index: Index<V>) -> Option<Self> {
276 let contig_id = 0;
277 let sites_left_on_contig = index.records().first()?.sites();
278
279 Some(Self {
280 index,
281 contig_id,
282 sites_left_on_contig,
283 })
284 }
285}
286
287fn eof_err(msg: &str) -> io::Error {
288 io::Error::new(io::ErrorKind::UnexpectedEof, msg)
289}
290
291fn data_err(msg: &str) -> io::Error {
292 io::Error::new(io::ErrorKind::InvalidData, msg)
293}