noodles_vcf/header/record/value/
collection.rs

1//! VCF header record value collection.
2
3use std::{error, fmt};
4
5use indexmap::IndexMap;
6
7use super::Value;
8use crate::header::record::value::{map, Map};
9
10/// A collection of VCF header record other values.
11#[derive(Clone, Debug, Eq, PartialEq)]
12pub enum Collection {
13    /// A list of unstructured strings.
14    Unstructured(Vec<String>),
15    /// A map of structured maps.
16    Structured(IndexMap<String, Map<map::Other>>),
17}
18
19impl Collection {
20    /// Returns whether the collection has values.
21    pub fn is_empty(&self) -> bool {
22        self.len() == 0
23    }
24
25    /// Returns the number of values in the collection.
26    pub fn len(&self) -> usize {
27        match self {
28            Self::Unstructured(list) => list.len(),
29            Self::Structured(map) => map.len(),
30        }
31    }
32
33    pub(crate) fn add(&mut self, value: Value) -> Result<(), AddError> {
34        match (self, value) {
35            (Self::Unstructured(list), Value::String(s)) => {
36                list.push(s);
37                Ok(())
38            }
39            (Self::Unstructured(_), Value::Map(..)) => Err(AddError::TypeMismatch {
40                actual: "structured",
41                expected: "unstructured",
42            }),
43            (Self::Structured(map), Value::Map(id, m)) => try_insert(map, id, m),
44            (Self::Structured(_), Value::String(_)) => Err(AddError::TypeMismatch {
45                actual: "unstructured",
46                expected: "structured",
47            }),
48        }
49    }
50}
51
52fn try_insert(
53    map: &mut IndexMap<String, Map<map::Other>>,
54    id: String,
55    m: Map<map::Other>,
56) -> Result<(), AddError> {
57    use indexmap::map::Entry;
58
59    match map.entry(id) {
60        Entry::Vacant(entry) => {
61            entry.insert(m);
62            Ok(())
63        }
64        Entry::Occupied(entry) => Err(AddError::DuplicateId(entry.key().into())),
65    }
66}
67
68/// An error returned when a value fails be added to a collection.
69#[derive(Clone, Debug, Eq, PartialEq)]
70pub enum AddError {
71    /// The value does not match the collection type.
72    TypeMismatch {
73        /// The value type.
74        actual: &'static str,
75        /// The collection type.
76        expected: &'static str,
77    },
78    /// An ID is duplicated.
79    DuplicateId(String),
80}
81
82impl error::Error for AddError {}
83
84impl fmt::Display for AddError {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        match self {
87            Self::TypeMismatch { actual, expected } => {
88                write!(f, "type mismatch: expected {expected}, got {actual}")
89            }
90            Self::DuplicateId(id) => write!(f, "duplicate ID: {id}"),
91        }
92    }
93}