noodles_bam/record/
data.rs1pub 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
14pub 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 pub fn is_empty(&self) -> bool {
24 self.0.is_empty()
25 }
26
27 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 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}