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}