noodles_sam/alignment/record_buf.rs
1//! Alignment record buffer.
2
3mod builder;
4mod cigar;
5mod convert;
6pub mod data;
7mod quality_scores;
8mod sequence;
9
10use std::io;
11
12use bstr::{BStr, BString};
13use noodles_core::Position;
14
15pub use self::{
16 builder::Builder, cigar::Cigar, data::Data, quality_scores::QualityScores, sequence::Sequence,
17};
18use super::{
19 record::{Flags, MappingQuality},
20 Record,
21};
22use crate::{
23 header::{
24 record::value::{map::ReferenceSequence, Map},
25 ReferenceSequences,
26 },
27 Header,
28};
29
30/// An alignment record buffer.
31#[derive(Clone, Debug, PartialEq)]
32pub struct RecordBuf {
33 name: Option<BString>,
34 flags: Flags,
35 reference_sequence_id: Option<usize>,
36 alignment_start: Option<Position>,
37 mapping_quality: Option<MappingQuality>,
38 cigar: Cigar,
39 mate_reference_sequence_id: Option<usize>,
40 mate_alignment_start: Option<Position>,
41 template_length: i32,
42 sequence: Sequence,
43 quality_scores: QualityScores,
44 data: Data,
45}
46
47impl RecordBuf {
48 /// Creates an alignment record builder.
49 ///
50 /// # Examples
51 ///
52 /// ```
53 /// use noodles_sam as sam;
54 /// let builder = sam::alignment::RecordBuf::builder();
55 /// ```
56 pub fn builder() -> Builder {
57 Builder::default()
58 }
59
60 /// Returns the name.
61 ///
62 /// # Examples
63 ///
64 /// ```
65 /// use noodles_sam as sam;
66 /// let record = sam::alignment::RecordBuf::default();
67 /// assert!(record.name().is_none());
68 /// ```
69 pub fn name(&self) -> Option<&BStr> {
70 self.name.as_ref().map(|name| name.as_ref())
71 }
72
73 /// Returns a mutable reference to the name.
74 ///
75 /// # Examples
76 ///
77 /// ```
78 /// use bstr::ByteSlice;
79 /// use noodles_sam as sam;
80 ///
81 /// let mut record = sam::alignment::RecordBuf::default();
82 /// *record.name_mut() = Some("r1".into());
83 ///
84 /// assert_eq!(record.name(), Some(b"r1".as_bstr()));
85 /// ```
86 pub fn name_mut(&mut self) -> &mut Option<BString> {
87 &mut self.name
88 }
89
90 /// Returns the flags.
91 ///
92 /// # Examples
93 ///
94 /// ```
95 /// use noodles_sam::{self as sam, alignment::record::Flags};
96 /// let record = sam::alignment::RecordBuf::default();
97 /// assert_eq!(record.flags(), Flags::UNMAPPED);
98 /// ```
99 pub fn flags(&self) -> Flags {
100 self.flags
101 }
102
103 /// Returns a mutable reference to the flags.
104 ///
105 /// # Examples
106 ///
107 /// ```
108 /// use noodles_sam::{self as sam, alignment::record::Flags};
109 /// let mut record = sam::alignment::RecordBuf::default();
110 /// *record.flags_mut() = Flags::empty();
111 /// assert!(record.flags().is_empty());
112 /// ```
113 pub fn flags_mut(&mut self) -> &mut Flags {
114 &mut self.flags
115 }
116
117 /// Returns the reference sequence ID.
118 ///
119 /// # Examples
120 ///
121 /// ```
122 /// use noodles_sam as sam;
123 /// let record = sam::alignment::RecordBuf::default();
124 /// assert!(record.reference_sequence_id().is_none());
125 /// ```
126 pub fn reference_sequence_id(&self) -> Option<usize> {
127 self.reference_sequence_id
128 }
129
130 /// Returns a mutable reference to the reference sequence ID.
131 ///
132 /// # Examples
133 ///
134 /// ```
135 /// use noodles_sam as sam;
136 /// let mut record = sam::alignment::RecordBuf::default();
137 /// *record.reference_sequence_id_mut() = Some(0);
138 /// assert_eq!(record.reference_sequence_id(), Some(0));
139 /// ```
140 pub fn reference_sequence_id_mut(&mut self) -> &mut Option<usize> {
141 &mut self.reference_sequence_id
142 }
143
144 /// Returns the alignment start.
145 ///
146 /// This position is 1-based, inclusive.
147 ///
148 /// # Examples
149 ///
150 /// ```
151 /// use noodles_sam as sam;
152 /// let record = sam::alignment::RecordBuf::default();
153 /// assert!(record.alignment_start().is_none());
154 /// ```
155 pub fn alignment_start(&self) -> Option<Position> {
156 self.alignment_start
157 }
158
159 /// Returns a mutable reference to the alignment start.
160 ///
161 /// This position is 1-based, inclusive.
162 ///
163 /// # Examples
164 ///
165 /// ```
166 /// use noodles_core::Position;
167 /// use noodles_sam as sam;
168 /// let mut record = sam::alignment::RecordBuf::default();
169 /// *record.alignment_start_mut() = Some(Position::MIN);
170 /// assert_eq!(record.alignment_start(), Some(Position::MIN));
171 /// ```
172 pub fn alignment_start_mut(&mut self) -> &mut Option<Position> {
173 &mut self.alignment_start
174 }
175
176 /// Returns the mapping quality.
177 ///
178 /// # Examples
179 ///
180 /// ```
181 /// use noodles_sam as sam;
182 /// let record = sam::alignment::RecordBuf::default();
183 /// assert!(record.mapping_quality().is_none());
184 /// ```
185 pub fn mapping_quality(&self) -> Option<MappingQuality> {
186 self.mapping_quality
187 }
188
189 /// Returns a mutable reference to the mapping quality.
190 ///
191 /// # Examples
192 ///
193 /// ```
194 /// use noodles_sam::{self as sam, alignment::record::MappingQuality};
195 /// let mut record = sam::alignment::RecordBuf::default();
196 /// *record.mapping_quality_mut() = Some(MappingQuality::MIN);
197 /// assert_eq!(record.mapping_quality(), Some(MappingQuality::MIN));
198 /// ```
199 pub fn mapping_quality_mut(&mut self) -> &mut Option<MappingQuality> {
200 &mut self.mapping_quality
201 }
202
203 /// Returns the CIGAR operations.
204 ///
205 /// # Examples
206 ///
207 /// ```
208 /// use noodles_sam as sam;
209 /// let record = sam::alignment::RecordBuf::default();
210 /// assert!(record.cigar().as_ref().is_empty());
211 /// ```
212 pub fn cigar(&self) -> &Cigar {
213 &self.cigar
214 }
215
216 /// Returns a mutable reference to the CIGAR operations.
217 ///
218 /// # Examples
219 ///
220 /// ```
221 /// use noodles_sam::{
222 /// self as sam,
223 /// alignment::{
224 /// record::cigar::{op::Kind, Op},
225 /// record_buf::Cigar,
226 /// },
227 /// };
228 ///
229 /// let cigar: Cigar = [Op::new(Kind::Match, 4)].into_iter().collect();
230 ///
231 /// let mut record = sam::alignment::RecordBuf::default();
232 /// *record.cigar_mut() = cigar.clone();
233 ///
234 /// assert_eq!(record.cigar(), &cigar);
235 /// ```
236 pub fn cigar_mut(&mut self) -> &mut Cigar {
237 &mut self.cigar
238 }
239
240 /// Returns the mate reference sequence ID.
241 ///
242 /// # Examples
243 ///
244 /// ```
245 /// use noodles_sam as sam;
246 /// let record = sam::alignment::RecordBuf::default();
247 /// assert!(record.mate_reference_sequence_id().is_none());
248 /// ```
249 pub fn mate_reference_sequence_id(&self) -> Option<usize> {
250 self.mate_reference_sequence_id
251 }
252
253 /// Returns a mutable reference to the mate reference sequence ID.
254 ///
255 /// # Examples
256 ///
257 /// ```
258 /// use noodles_sam as sam;
259 /// let mut record = sam::alignment::RecordBuf::default();
260 /// *record.mate_reference_sequence_id_mut() = Some(0);
261 /// assert_eq!(record.mate_reference_sequence_id(), Some(0));
262 /// ```
263 pub fn mate_reference_sequence_id_mut(&mut self) -> &mut Option<usize> {
264 &mut self.mate_reference_sequence_id
265 }
266
267 /// Returns the mate alignment start.
268 ///
269 /// This position is 1-based, inclusive.
270 ///
271 /// # Examples
272 ///
273 /// ```
274 /// use noodles_sam as sam;
275 /// let record = sam::alignment::RecordBuf::default();
276 /// assert!(record.mate_alignment_start().is_none());
277 /// ```
278 pub fn mate_alignment_start(&self) -> Option<Position> {
279 self.mate_alignment_start
280 }
281
282 /// Returns a mutable reference to the mate alignment start.
283 ///
284 /// This position is 1-based, inclusive.
285 ///
286 /// # Examples
287 ///
288 /// ```
289 /// use noodles_core::Position;
290 /// use noodles_sam as sam;
291 /// let mut record = sam::alignment::RecordBuf::default();
292 /// *record.mate_alignment_start_mut() = Some(Position::MIN);
293 /// assert_eq!(record.mate_alignment_start(), Some(Position::MIN));
294 /// ```
295 pub fn mate_alignment_start_mut(&mut self) -> &mut Option<Position> {
296 &mut self.mate_alignment_start
297 }
298
299 /// Returns the template length.
300 ///
301 /// # Examples
302 ///
303 /// ```
304 /// use noodles_sam as sam;
305 /// let record = sam::alignment::RecordBuf::default();
306 /// assert_eq!(record.template_length(), 0);
307 /// ```
308 pub fn template_length(&self) -> i32 {
309 self.template_length
310 }
311
312 /// Returns a mutable reference to template length.
313 ///
314 /// # Examples
315 ///
316 /// ```
317 /// use noodles_sam as sam;
318 /// let mut record = sam::alignment::RecordBuf::default();
319 /// *record.template_length_mut() = 4;
320 /// assert_eq!(record.template_length(), 4);
321 /// ```
322 pub fn template_length_mut(&mut self) -> &mut i32 {
323 &mut self.template_length
324 }
325
326 /// Returns the sequence.
327 ///
328 /// # Examples
329 ///
330 /// ```
331 /// use noodles_sam as sam;
332 /// let record = sam::alignment::RecordBuf::default();
333 /// assert!(record.sequence().is_empty());
334 /// ```
335 pub fn sequence(&self) -> &Sequence {
336 &self.sequence
337 }
338
339 /// Returns a mutable reference to sequence.
340 ///
341 /// # Examples
342 ///
343 /// ```
344 /// use noodles_sam::{self as sam, alignment::record_buf::Sequence};
345 ///
346 /// let sequence = Sequence::from(b"ACGT");
347 ///
348 /// let mut record = sam::alignment::RecordBuf::default();
349 /// *record.sequence_mut() = sequence.clone();
350 ///
351 /// assert_eq!(record.sequence(), &sequence);
352 /// ```
353 pub fn sequence_mut(&mut self) -> &mut Sequence {
354 &mut self.sequence
355 }
356
357 /// Returns the quality scores.
358 ///
359 /// # Examples
360 ///
361 /// ```
362 /// use noodles_sam as sam;
363 /// let record = sam::alignment::RecordBuf::default();
364 /// assert!(record.quality_scores().is_empty());
365 /// ```
366 pub fn quality_scores(&self) -> &QualityScores {
367 &self.quality_scores
368 }
369
370 /// Returns a mutable reference to quality scores.
371 ///
372 /// # Examples
373 ///
374 /// ```
375 /// use noodles_sam::{self as sam, alignment::record_buf::QualityScores};
376 ///
377 /// let quality_scores = QualityScores::default();
378 ///
379 /// let mut record = sam::alignment::RecordBuf::default();
380 /// *record.quality_scores_mut() = quality_scores.clone();
381 ///
382 /// assert_eq!(record.quality_scores(), &quality_scores);
383 /// ```
384 pub fn quality_scores_mut(&mut self) -> &mut QualityScores {
385 &mut self.quality_scores
386 }
387
388 /// Returns the data.
389 ///
390 /// # Examples
391 ///
392 /// ```
393 /// use noodles_sam as sam;
394 /// let record = sam::alignment::RecordBuf::default();
395 /// assert!(record.data().is_empty());
396 /// ```
397 pub fn data(&self) -> &Data {
398 &self.data
399 }
400
401 /// Returns a mutable reference to the data.
402 ///
403 /// # Examples
404 ///
405 /// ```
406 /// use noodles_sam::{
407 /// self as sam,
408 /// alignment::{
409 /// record::data::field::Tag,
410 /// record_buf::{data::field::Value, Data},
411 /// },
412 /// };
413 ///
414 /// let data: Data = [(Tag::ALIGNMENT_HIT_COUNT, Value::from(1))]
415 /// .into_iter()
416 /// .collect();
417 ///
418 /// let mut record = sam::alignment::RecordBuf::default();
419 /// *record.data_mut() = data.clone();
420 ///
421 /// assert_eq!(record.data_mut(), &data);
422 /// ```
423 pub fn data_mut(&mut self) -> &mut Data {
424 &mut self.data
425 }
426
427 /// Returns the associated reference sequence.
428 ///
429 /// # Examples
430 ///
431 /// ```
432 /// use noodles_sam as sam;
433 /// let header = sam::Header::default();
434 /// let record = sam::alignment::RecordBuf::default();
435 /// assert!(record.reference_sequence(&header).is_none());
436 /// ```
437 pub fn reference_sequence<'a>(
438 &self,
439 header: &'a Header,
440 ) -> Option<io::Result<(&'a [u8], &'a Map<ReferenceSequence>)>> {
441 get_reference_sequence(header.reference_sequences(), self.reference_sequence_id())
442 }
443
444 /// Returns the associated mate reference sequence.
445 ///
446 /// # Examples
447 ///
448 /// ```
449 /// use noodles_sam as sam;
450 /// let header = sam::Header::default();
451 /// let record = sam::alignment::RecordBuf::default();
452 /// assert!(record.mate_reference_sequence(&header).is_none());
453 /// ```
454 pub fn mate_reference_sequence<'a>(
455 &self,
456 header: &'a Header,
457 ) -> Option<io::Result<(&'a [u8], &'a Map<ReferenceSequence>)>> {
458 get_reference_sequence(
459 header.reference_sequences(),
460 self.mate_reference_sequence_id(),
461 )
462 }
463
464 /// Returns the alignment span.
465 ///
466 /// # Examples
467 ///
468 /// ```
469 /// use noodles_sam as sam;
470 /// let record = sam::alignment::RecordBuf::default();
471 /// assert!(record.alignment_span().is_none());
472 /// ```
473 pub fn alignment_span(&self) -> Option<usize> {
474 match self.cigar().alignment_span() {
475 0 => None,
476 span => Some(span),
477 }
478 }
479
480 /// Calculates the end position.
481 ///
482 /// # Examples
483 ///
484 /// ```
485 /// use noodles_core::Position;
486 /// use noodles_sam::{
487 /// self as sam,
488 /// alignment::record::cigar::{op::Kind, Op},
489 /// };
490 ///
491 /// let record = sam::alignment::RecordBuf::builder()
492 /// .set_alignment_start(Position::try_from(8)?)
493 /// .set_cigar([Op::new(Kind::Match, 5)].into_iter().collect())
494 /// .build();
495 ///
496 /// assert_eq!(record.alignment_end(), Position::new(12));
497 /// # Ok::<_, noodles_core::position::TryFromIntError>(())
498 /// ```
499 pub fn alignment_end(&self) -> Option<Position> {
500 self.alignment_start()
501 .and_then(|start| match self.alignment_span() {
502 Some(span) => {
503 let end = usize::from(start) + span - 1;
504 Position::new(end)
505 }
506 None => Some(start),
507 })
508 }
509}
510
511impl Record for RecordBuf {
512 fn name(&self) -> Option<&BStr> {
513 self.name()
514 }
515
516 fn flags(&self) -> io::Result<Flags> {
517 Ok(self.flags())
518 }
519
520 fn reference_sequence_id<'r, 'h: 'r>(&'r self, _: &'h Header) -> Option<io::Result<usize>> {
521 self.reference_sequence_id().map(Ok)
522 }
523
524 fn alignment_start(&self) -> Option<io::Result<Position>> {
525 self.alignment_start().map(Ok)
526 }
527
528 fn mapping_quality(&self) -> Option<io::Result<MappingQuality>> {
529 self.mapping_quality().map(Ok)
530 }
531
532 fn cigar(&self) -> Box<dyn super::record::Cigar + '_> {
533 Box::new(self.cigar())
534 }
535
536 fn mate_reference_sequence_id<'r, 'h: 'r>(
537 &'r self,
538 _: &'h Header,
539 ) -> Option<io::Result<usize>> {
540 self.mate_reference_sequence_id().map(Ok)
541 }
542
543 fn mate_alignment_start(&self) -> Option<io::Result<Position>> {
544 self.mate_alignment_start().map(Ok)
545 }
546
547 fn template_length(&self) -> io::Result<i32> {
548 Ok(self.template_length())
549 }
550
551 fn sequence(&self) -> Box<dyn super::record::Sequence + '_> {
552 Box::new(self.sequence())
553 }
554
555 fn quality_scores(&self) -> Box<dyn super::record::QualityScores + '_> {
556 Box::new(self.quality_scores())
557 }
558
559 fn data(&self) -> Box<dyn super::record::Data + '_> {
560 Box::new(self.data())
561 }
562}
563
564impl Default for RecordBuf {
565 fn default() -> Self {
566 Self::builder().build()
567 }
568}
569
570fn get_reference_sequence(
571 reference_sequences: &ReferenceSequences,
572 reference_sequence_id: Option<usize>,
573) -> Option<io::Result<(&[u8], &Map<ReferenceSequence>)>> {
574 reference_sequence_id.map(|id| {
575 reference_sequences
576 .get_index(id)
577 .map(|(name, map)| (name.as_ref(), map))
578 .ok_or_else(|| {
579 io::Error::new(io::ErrorKind::InvalidData, "invalid reference sequence ID")
580 })
581 })
582}