noodles_vcf/record/
info.rs

1mod field;
2
3use std::{io, iter};
4
5use self::field::parse_field;
6use crate::{variant::record::info::field::Value, Header};
7
8/// Raw VCF record info.
9#[derive(Debug, Eq, PartialEq)]
10pub struct Info<'r>(&'r str);
11
12impl<'r> Info<'r> {
13    pub(super) fn new(buf: &'r str) -> Self {
14        Self(buf)
15    }
16
17    /// Returns the value with the given key.
18    pub fn get<'h: 'r>(
19        &'r self,
20        header: &'h Header,
21        key: &str,
22    ) -> Option<io::Result<Option<Value<'r>>>> {
23        for result in self.iter(header) {
24            match result {
25                Ok((k, v)) => {
26                    if k == key {
27                        return Some(Ok(v));
28                    }
29                }
30                Err(e) => return Some(Err(e)),
31            }
32        }
33
34        None
35    }
36
37    /// Returns an iterator over all fields.
38    pub fn iter<'h: 'r>(
39        &'r self,
40        header: &'h Header,
41    ) -> impl Iterator<Item = io::Result<(&'r str, Option<Value<'r>>)>> + 'r {
42        let mut src = self.0;
43
44        iter::from_fn(move || {
45            if src.is_empty() {
46                None
47            } else {
48                Some(parse_field(&mut src, header))
49            }
50        })
51    }
52}
53
54impl AsRef<str> for Info<'_> {
55    fn as_ref(&self) -> &str {
56        self.0
57    }
58}
59
60impl crate::variant::record::Info for Info<'_> {
61    fn is_empty(&self) -> bool {
62        self.0.is_empty()
63    }
64
65    fn len(&self) -> usize {
66        const DELIMITER: u8 = b';';
67
68        if self.is_empty() {
69            0
70        } else {
71            let n = self
72                .0
73                .as_bytes()
74                .iter()
75                .filter(|&&b| b == DELIMITER)
76                .count();
77
78            n + 1
79        }
80    }
81
82    fn get<'a, 'h: 'a>(
83        &'a self,
84        header: &'h Header,
85        key: &str,
86    ) -> Option<io::Result<Option<Value<'a>>>> {
87        self.get(header, key)
88    }
89
90    fn iter<'a, 'h: 'a>(
91        &'a self,
92        header: &'h Header,
93    ) -> Box<dyn Iterator<Item = io::Result<(&'a str, Option<Value<'a>>)>> + 'a> {
94        Box::new(self.iter(header))
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use crate::variant::record::Info as _;
102
103    #[test]
104    fn test_is_empty() {
105        assert!(Info::new("").is_empty());
106        assert!(!Info::new("NS=2;DP=.").is_empty());
107    }
108
109    #[test]
110    fn test_iter() {
111        use crate::variant::record::info::field::key;
112
113        let header = Header::default();
114
115        let info = Info::new("");
116        assert!(info.iter(&header).next().is_none());
117
118        let info = Info::new("NS=2;DP=.");
119        let mut iter = info.iter(&header);
120
121        assert!(matches!(
122            iter.next(),
123            Some(Ok((key::SAMPLES_WITH_DATA_COUNT, Some(Value::Integer(2)))))
124        ));
125
126        assert!(matches!(iter.next(), Some(Ok((key::TOTAL_DEPTH, None)))));
127    }
128}