noodles_sam/io/reader/
query.rs1mod records;
2
3use std::io;
4
5use noodles_bgzf as bgzf;
6use noodles_core::region::Interval;
7use noodles_csi::{self as csi, binning_index::index::reference_sequence::bin::Chunk};
8
9use self::records::Records;
10use super::Reader;
11use crate::{Header, Record, alignment::Record as _};
12
13pub struct Query<'r, 'h: 'r, R> {
17 reader: Reader<csi::io::Query<'r, R>>,
18 header: &'h Header,
19 reference_sequence_id: usize,
20 interval: Interval,
21}
22
23impl<'r, 'h: 'r, R> Query<'r, 'h, R>
24where
25 R: bgzf::io::BufRead + bgzf::io::Seek,
26{
27 pub(super) fn new(
28 reader: &'r mut R,
29 chunks: Vec<Chunk>,
30 header: &'h Header,
31 reference_sequence_id: usize,
32 interval: Interval,
33 ) -> Self {
34 Self {
35 reader: Reader::new(csi::io::Query::new(reader, chunks)),
36 header,
37 reference_sequence_id,
38 interval,
39 }
40 }
41
42 pub fn read_record(&mut self, record: &mut Record) -> io::Result<usize> {
70 next_record(
71 &mut self.reader,
72 record,
73 self.header,
74 self.reference_sequence_id,
75 self.interval,
76 )
77 }
78
79 pub fn records(self) -> Records<'r, 'h, R> {
81 Records::new(self)
82 }
83}
84
85pub(crate) fn intersects(
86 header: &Header,
87 record: &Record,
88 reference_sequence_id: usize,
89 region_interval: Interval,
90) -> io::Result<bool> {
91 let Some(id) = record.reference_sequence_id(header).transpose()? else {
92 return Ok(false);
93 };
94
95 if id != reference_sequence_id {
96 return Ok(false);
97 }
98
99 if interval_is_unbounded(region_interval) {
100 Ok(true)
101 } else {
102 match (
103 record.alignment_start().transpose()?,
104 record.alignment_end().transpose()?,
105 ) {
106 (Some(start), Some(end)) => {
107 let alignment_interval = (start..=end).into();
108 Ok(region_interval.intersects(alignment_interval))
109 }
110 _ => Ok(false),
111 }
112 }
113}
114
115fn interval_is_unbounded(interval: Interval) -> bool {
116 interval.start().is_none() && interval.end().is_none()
117}
118
119fn next_record<R>(
120 reader: &mut Reader<csi::io::Query<'_, R>>,
121 record: &mut Record,
122 header: &Header,
123 reference_sequence_id: usize,
124 interval: Interval,
125) -> io::Result<usize>
126where
127 R: bgzf::io::BufRead + bgzf::io::Seek,
128{
129 loop {
130 match reader.read_record(record)? {
131 0 => return Ok(0),
132 n => {
133 if intersects(header, record, reference_sequence_id, interval)? {
134 return Ok(n);
135 }
136 }
137 }
138 }
139}