noodles_sam/alignment/record_buf/data.rs
1//! Alignment record data buffer.
2
3pub mod field;
4
5use std::{io, mem};
6
7use self::field::Value;
8use crate::alignment::record::data::field::Tag;
9
10/// An alignment record data buffer.
11#[derive(Debug, Default, Clone, PartialEq)]
12pub struct Data(Vec<(Tag, Value)>);
13
14impl Data {
15 /// Returns the number of fields.
16 ///
17 /// # Examples
18 ///
19 /// ```
20 /// use noodles_sam::alignment::record_buf::Data;
21 /// let data = Data::default();
22 /// assert_eq!(data.len(), 0);
23 /// ```
24 pub fn len(&self) -> usize {
25 self.0.len()
26 }
27
28 /// Returns whether there are any fields.
29 ///
30 /// # Examples
31 ///
32 /// ```
33 /// use noodles_sam::alignment::record_buf::Data;
34 /// let data = Data::default();
35 /// assert!(data.is_empty());
36 /// ```
37 pub fn is_empty(&self) -> bool {
38 self.0.is_empty()
39 }
40
41 /// Removes all fields from the map.
42 ///
43 /// This does not affect the internal capacity.
44 ///
45 /// # Examples
46 ///
47 /// ```
48 /// use noodles_sam::alignment::{
49 /// record::data::field::Tag,
50 /// record_buf::{data::field::Value, Data},
51 /// };
52 ///
53 /// let nh = (Tag::ALIGNMENT_HIT_COUNT, Value::from(1));
54 /// let mut data: Data = [nh].into_iter().collect();
55 /// assert_eq!(data.len(), 1);
56 /// data.clear();
57 /// assert!(data.is_empty());
58 /// ```
59 pub fn clear(&mut self) {
60 self.0.clear();
61 }
62
63 /// Returns a reference to the value of the given tag.
64 ///
65 /// # Examples
66 ///
67 /// ```
68 /// use noodles_sam::alignment::{
69 /// record::data::field::Tag,
70 /// record_buf::{data::field::Value, Data},
71 /// };
72 ///
73 /// let (tag, value) = (Tag::ALIGNMENT_HIT_COUNT, Value::from(1));
74 /// let data: Data = [(tag, value.clone())].into_iter().collect();
75 ///
76 /// assert_eq!(data.get(&tag), Some(&value));
77 /// assert!(data.get(&Tag::READ_GROUP).is_none());
78 /// ```
79 pub fn get<K>(&self, tag: &K) -> Option<&Value>
80 where
81 K: indexmap::Equivalent<Tag>,
82 {
83 self.0
84 .iter()
85 .find(|(t, _)| tag.equivalent(t))
86 .map(|(_, v)| v)
87 }
88
89 /// Returns a mutable reference to the value of the given tag.
90 ///
91 /// # Examples
92 ///
93 /// ```
94 /// use noodles_sam::alignment::{
95 /// record::data::field::Tag,
96 /// record_buf::{data::field::Value, Data},
97 /// };
98 ///
99 /// let tag = Tag::ALIGNMENT_HIT_COUNT;
100 /// let mut data: Data = [(tag, Value::from(1))].into_iter().collect();
101 ///
102 /// if let Some(v) = data.get_mut(&tag) {
103 /// *v = Value::from(2);
104 /// }
105 ///
106 /// assert_eq!(data.get(&tag), Some(&Value::from(2)));
107 /// ```
108 pub fn get_mut<K>(&mut self, tag: &K) -> Option<&mut Value>
109 where
110 K: indexmap::Equivalent<Tag>,
111 {
112 self.0
113 .iter_mut()
114 .find(|(t, _)| tag.equivalent(t))
115 .map(|(_, v)| v)
116 }
117
118 /// Returns the index of the field of the given tag.
119 ///
120 /// # Examples
121 ///
122 /// ```
123 /// use noodles_sam::alignment::{
124 /// record::data::field::Tag,
125 /// record_buf::{data::field::Value, Data},
126 /// };
127 ///
128 /// let nh = (Tag::ALIGNMENT_HIT_COUNT, Value::from(1));
129 /// let data: Data = [nh].into_iter().collect();
130 ///
131 /// assert_eq!(data.get_index_of(&Tag::ALIGNMENT_HIT_COUNT), Some(0));
132 /// assert!(data.get_index_of(&Tag::READ_GROUP).is_none());
133 /// ```
134 pub fn get_index_of<K>(&self, tag: &K) -> Option<usize>
135 where
136 K: indexmap::Equivalent<Tag>,
137 {
138 self.0.iter().position(|(t, _)| tag.equivalent(t))
139 }
140
141 /// Returns an iterator over all tag-value pairs.
142 ///
143 /// # Examples
144 ///
145 /// ```
146 /// use noodles_sam::alignment::{
147 /// record::data::field::Tag,
148 /// record_buf::{data::field::Value, Data},
149 /// };
150 ///
151 /// let (tag, value) = (Tag::ALIGNMENT_HIT_COUNT, Value::from(1));
152 /// let data: Data = [(tag, value.clone())].into_iter().collect();
153 ///
154 /// let mut fields = data.iter();
155 /// assert_eq!(fields.next(), Some((tag, &value)));
156 /// assert!(fields.next().is_none());
157 /// ```
158 pub fn iter(&self) -> impl Iterator<Item = (Tag, &Value)> {
159 self.0.iter().map(|(tag, value)| (*tag, value))
160 }
161
162 /// Returns an iterator over all tags.
163 ///
164 /// # Examples
165 ///
166 /// ```
167 /// use noodles_sam::alignment::{
168 /// record::data::field::Tag,
169 /// record_buf::{data::field::Value, Data},
170 /// };
171 ///
172 /// let nh = (Tag::ALIGNMENT_HIT_COUNT, Value::from(1));
173 /// let data: Data = [nh].into_iter().collect();
174 ///
175 /// let mut keys = data.keys();
176 /// assert_eq!(keys.next(), Some(Tag::ALIGNMENT_HIT_COUNT));
177 /// assert!(keys.next().is_none());
178 /// ```
179 pub fn keys(&self) -> impl Iterator<Item = Tag> + '_ {
180 self.0.iter().map(|(tag, _)| *tag)
181 }
182
183 /// Returns an iterator over all values.
184 ///
185 /// # Examples
186 ///
187 /// ```
188 /// use noodles_sam::alignment::{
189 /// record::data::field::Tag,
190 /// record_buf::{data::field::Value, Data},
191 /// };
192 ///
193 /// let (tag, value) = (Tag::ALIGNMENT_HIT_COUNT, Value::from(1));
194 /// let data: Data = [(tag, value.clone())].into_iter().collect();
195 ///
196 /// let mut values = data.values();
197 /// assert_eq!(values.next(), Some(&value));
198 /// assert!(values.next().is_none());
199 /// ```
200 pub fn values(&self) -> impl Iterator<Item = &Value> {
201 self.0.iter().map(|(_, value)| value)
202 }
203
204 /// Inserts a field into the map.
205 ///
206 /// This uses the field tag as the key and field as the value.
207 ///
208 /// If the tag already exists in the map, the existing field is replaced by the new one, and
209 /// the existing field is returned.
210 ///
211 /// # Examples
212 ///
213 /// ```
214 /// use noodles_sam::alignment::{
215 /// record::data::field::Tag,
216 /// record_buf::{data::field::Value, Data},
217 /// };
218 ///
219 /// let mut data = Data::default();
220 /// data.insert(Tag::ALIGNMENT_HIT_COUNT, Value::from(1));
221 /// ```
222 pub fn insert(&mut self, tag: Tag, value: Value) -> Option<(Tag, Value)> {
223 let field = (tag, value);
224
225 match self.get_index_of(&tag) {
226 Some(i) => Some(mem::replace(&mut self.0[i], field)),
227 None => {
228 self.0.push(field);
229 None
230 }
231 }
232 }
233
234 /// Removes the field with the given tag.
235 ///
236 /// The field is returned if it exists.
237 ///
238 /// This works like [`Vec::swap_remove`]; it does not preserve the order but has a constant
239 /// time complexity.
240 ///
241 /// # Examples
242 ///
243 /// ```
244 /// use noodles_sam::alignment::{
245 /// record::data::field::Tag,
246 /// record_buf::{data::field::Value, Data},
247 /// };
248 ///
249 /// let nh = (Tag::ALIGNMENT_HIT_COUNT, Value::from(1));
250 /// let rg = (Tag::READ_GROUP, Value::from("rg0"));
251 /// let md = (Tag::ALIGNMENT_SCORE, Value::from(98));
252 /// let mut data: Data = [nh.clone(), rg.clone(), md.clone()].into_iter().collect();
253 ///
254 /// assert_eq!(data.remove(&Tag::ALIGNMENT_HIT_COUNT), Some(nh));
255 /// assert!(data.remove(&Tag::COMMENT).is_none());
256 ///
257 /// let expected = [md, rg].into_iter().collect();
258 /// assert_eq!(data, expected);
259 /// ```
260 pub fn remove<K>(&mut self, tag: &K) -> Option<(Tag, Value)>
261 where
262 K: indexmap::Equivalent<Tag>,
263 {
264 self.swap_remove(tag)
265 }
266
267 fn swap_remove<K>(&mut self, tag: &K) -> Option<(Tag, Value)>
268 where
269 K: indexmap::Equivalent<Tag>,
270 {
271 self.get_index_of(tag).map(|i| self.0.swap_remove(i))
272 }
273}
274
275impl crate::alignment::record::Data for &Data {
276 fn is_empty(&self) -> bool {
277 Data::is_empty(self)
278 }
279
280 fn get(
281 &self,
282 tag: &Tag,
283 ) -> Option<io::Result<crate::alignment::record::data::field::Value<'_>>> {
284 Data::get(self, tag).map(|value| Ok(value.into()))
285 }
286
287 fn iter(
288 &self,
289 ) -> Box<
290 dyn Iterator<Item = io::Result<(Tag, crate::alignment::record::data::field::Value<'_>)>>
291 + '_,
292 > {
293 Box::new(Data::iter(self).map(|(tag, value)| Ok((tag, value.into()))))
294 }
295}
296
297impl crate::alignment::record::Data for Data {
298 fn is_empty(&self) -> bool {
299 self.is_empty()
300 }
301
302 fn get(
303 &self,
304 tag: &Tag,
305 ) -> Option<io::Result<crate::alignment::record::data::field::Value<'_>>> {
306 self.get(tag).map(|value| Ok(value.into()))
307 }
308
309 fn iter(
310 &self,
311 ) -> Box<
312 dyn Iterator<Item = io::Result<(Tag, crate::alignment::record::data::field::Value<'_>)>>
313 + '_,
314 > {
315 Box::new(self.iter().map(|(tag, value)| Ok((tag, value.into()))))
316 }
317}
318
319impl Extend<(Tag, Value)> for Data {
320 fn extend<T: IntoIterator<Item = (Tag, Value)>>(&mut self, iter: T) {
321 for (tag, value) in iter {
322 self.insert(tag, value);
323 }
324 }
325}
326
327impl FromIterator<(Tag, Value)> for Data {
328 fn from_iter<T: IntoIterator<Item = (Tag, Value)>>(iter: T) -> Self {
329 let mut data = Self::default();
330 data.extend(iter);
331 data
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338
339 #[test]
340 fn test_remove_with_multiple_removes() {
341 let zz = Tag::new(b'z', b'z');
342
343 let mut data: Data = [
344 (Tag::ALIGNMENT_HIT_COUNT, Value::from(2)),
345 (Tag::EDIT_DISTANCE, Value::from(1)),
346 (zz, Value::from(0)),
347 ]
348 .into_iter()
349 .collect();
350
351 data.remove(&Tag::EDIT_DISTANCE);
352 data.remove(&zz);
353 data.remove(&Tag::ALIGNMENT_HIT_COUNT);
354
355 assert!(data.is_empty());
356 }
357}