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}