noodles_sam/record/
data.rs

1//! SAM record data.
2
3pub mod field;
4
5use std::{borrow::Borrow, fmt, io, iter};
6
7use self::field::parse_field;
8use crate::alignment::record::data::field::{Tag, Value};
9
10/// SAM record data.
11pub struct Data<'a>(&'a [u8]);
12
13impl<'a> Data<'a> {
14    pub(super) fn new(buf: &'a [u8]) -> Self {
15        Self(buf)
16    }
17
18    /// Returns whether there are any data fields.
19    pub fn is_empty(&self) -> bool {
20        self.0.is_empty()
21    }
22
23    /// Returns the value for the given tag.
24    pub fn get<K>(&self, tag: &K) -> Option<io::Result<Value<'_>>>
25    where
26        K: Borrow<[u8; 2]>,
27    {
28        for result in self.iter() {
29            match result {
30                Ok((t, value)) => {
31                    if &t == tag.borrow() {
32                        return Some(Ok(value));
33                    }
34                }
35                Err(e) => return Some(Err(e)),
36            }
37        }
38
39        None
40    }
41
42    /// Returns an iterator over all tag-value pairs.
43    pub fn iter(&self) -> impl Iterator<Item = io::Result<(Tag, Value<'_>)>> + '_ {
44        let mut src = self.0;
45
46        iter::from_fn(move || {
47            if src.is_empty() {
48                None
49            } else {
50                Some(parse_field(&mut src))
51            }
52        })
53    }
54}
55
56impl fmt::Debug for Data<'_> {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        let mut formatter = f.debug_map();
59
60        for result in self.iter() {
61            let (tag, value) = result.map_err(|_| fmt::Error)?;
62            formatter.entry(&tag, &value);
63        }
64
65        formatter.finish()
66    }
67}
68
69impl crate::alignment::record::Data for Data<'_> {
70    fn is_empty(&self) -> bool {
71        self.is_empty()
72    }
73
74    fn get(&self, tag: &Tag) -> Option<io::Result<Value<'_>>> {
75        self.get(tag)
76    }
77
78    fn iter(&self) -> Box<dyn Iterator<Item = io::Result<(Tag, Value<'_>)>> + '_> {
79        Box::new(self.iter())
80    }
81}
82
83impl AsRef<[u8]> for Data<'_> {
84    fn as_ref(&self) -> &[u8] {
85        self.0
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_is_empty() {
95        let data = Data::new(b"");
96        assert!(data.is_empty());
97
98        let data = Data::new(b"NH:i:1");
99        assert!(!data.is_empty());
100    }
101
102    #[test]
103    fn test_get() -> io::Result<()> {
104        let data = Data::new(b"");
105        assert!(data.get(&Tag::ALIGNMENT_HIT_COUNT).is_none());
106        assert!(data.get(&Tag::COMMENT).is_none());
107
108        let data = Data::new(b"NH:i:1");
109        assert!(matches!(
110            data.get(&Tag::ALIGNMENT_HIT_COUNT).transpose()?,
111            Some(Value::Int32(1))
112        ));
113        assert!(data.get(&Tag::COMMENT).is_none());
114
115        let data = Data::new(b"NH:i:1\tCO:Z:ndls");
116        assert!(matches!(
117            data.get(&Tag::ALIGNMENT_HIT_COUNT).transpose()?,
118            Some(Value::Int32(1))
119        ));
120        assert!(matches!(
121            data.get(&Tag::COMMENT).transpose()?,
122            Some(Value::String(s)) if s == b"ndls"
123        ));
124
125        Ok(())
126    }
127
128    #[test]
129    fn test_iter() -> io::Result<()> {
130        let data = Data::new(b"");
131        assert!(data.iter().next().is_none());
132
133        let data = Data::new(b"NH:i:1");
134        let actual: Vec<_> = data.iter().collect::<io::Result<_>>()?;
135
136        assert_eq!(actual.len(), 1);
137
138        let (actual_tag, actual_value) = &actual[0];
139        assert_eq!(actual_tag, &Tag::ALIGNMENT_HIT_COUNT);
140        assert!(matches!(actual_value, Value::Int32(1)));
141
142        Ok(())
143    }
144}