noodles_vcf/record/samples/
sample.rs

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