noodles_sam/alignment/
record.rs1pub mod cigar;
4pub mod data;
5mod flags;
6pub mod mapping_quality;
7mod quality_scores;
8mod sequence;
9
10use std::io;
11
12use bstr::BStr;
13use noodles_core::Position;
14
15pub use self::{
16 cigar::Cigar, data::Data, flags::Flags, mapping_quality::MappingQuality,
17 quality_scores::QualityScores, sequence::Sequence,
18};
19use crate::{
20 header::{
21 record::value::{map::ReferenceSequence, Map},
22 ReferenceSequences,
23 },
24 Header,
25};
26
27pub trait Record {
29 fn name(&self) -> Option<&BStr>;
31
32 fn flags(&self) -> io::Result<Flags>;
34
35 fn reference_sequence_id<'r, 'h: 'r>(&'r self, header: &'h Header)
37 -> Option<io::Result<usize>>;
38
39 fn alignment_start(&self) -> Option<io::Result<Position>>;
43
44 fn mapping_quality(&self) -> Option<io::Result<MappingQuality>>;
46
47 fn cigar(&self) -> Box<dyn Cigar + '_>;
49
50 fn mate_reference_sequence_id<'r, 'h: 'r>(
52 &'r self,
53 header: &'h Header,
54 ) -> Option<io::Result<usize>>;
55
56 fn mate_alignment_start(&self) -> Option<io::Result<Position>>;
60
61 fn template_length(&self) -> io::Result<i32>;
63
64 fn sequence(&self) -> Box<dyn Sequence + '_>;
66
67 fn quality_scores(&self) -> Box<dyn QualityScores + '_>;
69
70 fn data(&self) -> Box<dyn Data + '_>;
72
73 fn reference_sequence<'h>(
75 &self,
76 header: &'h Header,
77 ) -> Option<io::Result<(&'h BStr, &'h Map<ReferenceSequence>)>> {
78 let reference_sequence_id = match self.reference_sequence_id(header).transpose() {
79 Ok(reference_sequence_id) => reference_sequence_id,
80 Err(e) => return Some(Err(e)),
81 };
82
83 get_reference_sequence(header.reference_sequences(), reference_sequence_id)
84 }
85
86 fn mate_reference_sequence<'h>(
88 &self,
89 header: &'h Header,
90 ) -> Option<io::Result<(&'h BStr, &'h Map<ReferenceSequence>)>> {
91 let mate_reference_sequence_id = match self.mate_reference_sequence_id(header).transpose() {
92 Ok(id) => id,
93 Err(e) => return Some(Err(e)),
94 };
95
96 get_reference_sequence(header.reference_sequences(), mate_reference_sequence_id)
97 }
98
99 fn alignment_span(&self) -> Option<io::Result<usize>> {
101 match self.cigar().alignment_span() {
102 Ok(0) => None,
103 Ok(span) => Some(Ok(span)),
104 Err(e) => Some(Err(e)),
105 }
106 }
107
108 fn alignment_end(&self) -> Option<io::Result<Position>> {
112 let start = match self.alignment_start().transpose() {
113 Ok(position) => position?,
114 Err(e) => return Some(Err(e)),
115 };
116
117 match self.alignment_span() {
118 Some(Ok(span)) => {
119 let end = usize::from(start) + span - 1;
120 Position::new(end).map(Ok)
121 }
122 Some(Err(e)) => Some(Err(e)),
123 None => Some(Ok(start)),
124 }
125 }
126}
127
128impl Record for Box<dyn Record> {
129 fn name(&self) -> Option<&BStr> {
130 (**self).name()
131 }
132
133 fn flags(&self) -> io::Result<Flags> {
134 (**self).flags()
135 }
136
137 fn reference_sequence_id<'r, 'h: 'r>(
138 &'r self,
139 header: &'h Header,
140 ) -> Option<io::Result<usize>> {
141 (**self).reference_sequence_id(header)
142 }
143
144 fn alignment_start(&self) -> Option<io::Result<Position>> {
145 (**self).alignment_start()
146 }
147
148 fn mapping_quality(&self) -> Option<io::Result<MappingQuality>> {
149 (**self).mapping_quality()
150 }
151
152 fn cigar(&self) -> Box<dyn Cigar + '_> {
153 (**self).cigar()
154 }
155
156 fn mate_reference_sequence_id<'r, 'h: 'r>(
157 &'r self,
158 header: &'h Header,
159 ) -> Option<io::Result<usize>> {
160 (**self).mate_reference_sequence_id(header)
161 }
162
163 fn mate_alignment_start(&self) -> Option<io::Result<Position>> {
164 (**self).mate_alignment_start()
165 }
166
167 fn template_length(&self) -> io::Result<i32> {
168 (**self).template_length()
169 }
170
171 fn sequence(&self) -> Box<dyn Sequence + '_> {
172 (**self).sequence()
173 }
174
175 fn quality_scores(&self) -> Box<dyn QualityScores + '_> {
176 (**self).quality_scores()
177 }
178
179 fn data(&self) -> Box<dyn Data + '_> {
180 (**self).data()
181 }
182}
183
184fn get_reference_sequence(
185 reference_sequences: &ReferenceSequences,
186 reference_sequence_id: Option<usize>,
187) -> Option<io::Result<(&BStr, &Map<ReferenceSequence>)>> {
188 let id = reference_sequence_id?;
189
190 let result = reference_sequences
191 .get_index(id)
192 .map(|(name, reference_sequence)| (name.as_ref(), reference_sequence))
193 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "invalid reference sequence ID"));
194
195 Some(result)
196}
197
198#[cfg(test)]
199mod tests {
200 use super::*;
201
202 #[test]
203 fn test_alignment_end() -> Result<(), Box<dyn std::error::Error>> {
204 use crate::alignment::{
205 record::cigar::{op::Kind, Op},
206 RecordBuf,
207 };
208
209 let record = RecordBuf::builder()
210 .set_alignment_start(Position::try_from(8)?)
211 .set_cigar([Op::new(Kind::Match, 5)].into_iter().collect())
212 .build();
213
214 let actual = Record::alignment_end(&record).transpose()?;
215 let expected = Position::new(12);
216 assert_eq!(actual, expected);
217
218 Ok(())
219 }
220}