noodles_bam/record/
data.rs

1//! BAM record data.
2
3pub mod field;
4
5use std::{borrow::Borrow, fmt, io, iter};
6
7use noodles_sam::{
8    self as sam,
9    alignment::record::data::field::{Tag, Value},
10};
11
12use self::field::decode_field;
13
14/// BAM record data.
15pub struct Data<'a>(&'a [u8]);
16
17impl<'a> Data<'a> {
18    pub(super) fn new(src: &'a [u8]) -> Self {
19        Self(src)
20    }
21
22    /// Returns whether there are any fields.
23    pub fn is_empty(&self) -> bool {
24        self.0.is_empty()
25    }
26
27    /// Returns the value of the given tag.
28    pub fn get<K>(&self, tag: &K) -> Option<io::Result<Value<'_>>>
29    where
30        K: Borrow<[u8; 2]>,
31    {
32        for result in self.iter() {
33            match result {
34                Ok((t, value)) => {
35                    if &t == tag.borrow() {
36                        return Some(Ok(value));
37                    }
38                }
39                Err(e) => return Some(Err(e)),
40            };
41        }
42
43        None
44    }
45
46    /// Returns an iterator over all tag-value pairs.
47    pub fn iter(&self) -> impl Iterator<Item = io::Result<(Tag, Value<'_>)>> + '_ {
48        let mut src = self.0;
49
50        iter::from_fn(move || {
51            if src.is_empty() {
52                None
53            } else {
54                Some(decode_field(&mut src))
55            }
56        })
57    }
58}
59
60impl fmt::Debug for Data<'_> {
61    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
62        let mut formatter = f.debug_map();
63
64        for result in self.iter() {
65            let (tag, value) = result.map_err(|_| fmt::Error)?;
66            formatter.entry(&tag, &value);
67        }
68
69        formatter.finish()
70    }
71}
72
73impl sam::alignment::record::Data for Data<'_> {
74    fn is_empty(&self) -> bool {
75        self.is_empty()
76    }
77
78    fn get(&self, tag: &Tag) -> Option<io::Result<Value<'_>>> {
79        self.get(tag)
80    }
81
82    fn iter(&self) -> Box<dyn Iterator<Item = io::Result<(Tag, Value<'_>)>> + '_> {
83        Box::new(self.iter())
84    }
85}
86
87impl AsRef<[u8]> for Data<'_> {
88    fn as_ref(&self) -> &[u8] {
89        self.0
90    }
91}
92
93impl<'a> TryFrom<Data<'a>> for sam::alignment::record_buf::Data {
94    type Error = io::Error;
95
96    fn try_from(bam_data: Data<'a>) -> Result<Self, Self::Error> {
97        use crate::record::codec::decoder::read_data;
98
99        let mut src = bam_data.0;
100        let mut sam_data = Self::default();
101        read_data(&mut src, &mut sam_data)
102            .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
103
104        Ok(sam_data)
105    }
106}
107
108pub(super) fn get_raw_cigar<'a>(src: &mut &'a [u8]) -> io::Result<Option<&'a [u8]>> {
109    use noodles_sam::alignment::record::data::field::Type;
110
111    use self::field::{
112        decode_tag, decode_type, decode_value,
113        value::array::{decode_raw_array, decode_subtype},
114    };
115
116    fn get_array_field<'a>(src: &mut &'a [u8]) -> io::Result<Option<(Tag, &'a [u8])>> {
117        let tag = decode_tag(src)?;
118        let ty = decode_type(src)?;
119
120        if ty == Type::Array {
121            let subtype = decode_subtype(src)?;
122            let buf = decode_raw_array(src, subtype)?;
123            Ok(Some((tag, buf)))
124        } else {
125            decode_value(src, ty)?;
126            Ok(None)
127        }
128    }
129
130    while !src.is_empty() {
131        if let Some((Tag::CIGAR, buf)) = get_array_field(src)? {
132            return Ok(Some(buf));
133        }
134    }
135
136    Ok(None)
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_is_empty() {
145        let data = Data::new(&[]);
146        assert!(data.is_empty());
147
148        let data = Data::new(&[b'N', b'H', b'C', 0x01]);
149        assert!(!data.is_empty());
150    }
151
152    #[test]
153    fn test_get() -> io::Result<()> {
154        let data = Data::new(&[b'N', b'H', b'C', 0x01]);
155
156        assert!(data.get(&Tag::ALIGNMENT_HIT_COUNT).is_some());
157        assert!(data.get(b"NH").is_some());
158
159        assert!(data.get(&Tag::COMMENT).is_none());
160
161        Ok(())
162    }
163
164    #[test]
165    fn test_iter() -> io::Result<()> {
166        let data = Data::new(&[]);
167        assert!(data.iter().next().is_none());
168
169        let data = Data::new(&[b'N', b'H', b'C', 0x01]);
170        let actual: Vec<_> = data.iter().collect::<io::Result<_>>()?;
171
172        assert_eq!(actual.len(), 1);
173
174        let (actual_tag, actual_value) = &actual[0];
175        assert_eq!(actual_tag, &Tag::ALIGNMENT_HIT_COUNT);
176        assert!(matches!(actual_value, Value::UInt8(1)));
177
178        Ok(())
179    }
180}