Skip to main content

geam_core/host/component/
configuration.rs

1use ecow::EcoString;
2use std::collections::BTreeMap;
3
4/// Owned, read-only configuration supplied to one provider component.
5#[derive(Debug, Clone, PartialEq)]
6pub struct HostProviderConfiguration {
7    values: BTreeMap<EcoString, HostProviderConfigurationValue>,
8}
9
10/// Portable value families accepted by provider component configuration.
11#[derive(Debug, Clone, PartialEq)]
12pub enum HostProviderConfigurationValue {
13    String(EcoString),
14    Integer(i64),
15    Float(f64),
16    Bool(bool),
17    Array(Vec<Self>),
18    Table(HostProviderConfiguration),
19}
20
21impl HostProviderConfiguration {
22    pub fn new(values: BTreeMap<EcoString, HostProviderConfigurationValue>) -> Self {
23        Self { values }
24    }
25
26    pub fn empty() -> Self {
27        Self::new(BTreeMap::new())
28    }
29
30    pub fn get(&self, key: &str) -> Option<&HostProviderConfigurationValue> {
31        self.values.get(key)
32    }
33
34    pub fn iter(
35        &self,
36    ) -> impl ExactSizeIterator<Item = (&EcoString, &HostProviderConfigurationValue)> {
37        self.values.iter()
38    }
39
40    pub fn is_empty(&self) -> bool {
41        self.values.is_empty()
42    }
43}
44
45impl HostProviderConfigurationValue {
46    pub fn as_string(&self) -> Option<&EcoString> {
47        match self {
48            Self::String(value) => Some(value),
49            _ => None,
50        }
51    }
52
53    pub fn as_integer(&self) -> Option<i64> {
54        match self {
55            Self::Integer(value) => Some(*value),
56            _ => None,
57        }
58    }
59
60    pub fn as_float(&self) -> Option<f64> {
61        match self {
62            Self::Float(value) => Some(*value),
63            _ => None,
64        }
65    }
66
67    pub fn as_bool(&self) -> Option<bool> {
68        match self {
69            Self::Bool(value) => Some(*value),
70            _ => None,
71        }
72    }
73
74    pub fn as_array(&self) -> Option<&[Self]> {
75        match self {
76            Self::Array(value) => Some(value),
77            _ => None,
78        }
79    }
80
81    pub fn as_table(&self) -> Option<&HostProviderConfiguration> {
82        match self {
83            Self::Table(value) => Some(value),
84            _ => None,
85        }
86    }
87}
88
89impl From<EcoString> for HostProviderConfigurationValue {
90    fn from(value: EcoString) -> Self {
91        Self::String(value)
92    }
93}
94
95impl From<&str> for HostProviderConfigurationValue {
96    fn from(value: &str) -> Self {
97        Self::String(value.into())
98    }
99}
100
101impl From<i64> for HostProviderConfigurationValue {
102    fn from(value: i64) -> Self {
103        Self::Integer(value)
104    }
105}
106
107impl From<f64> for HostProviderConfigurationValue {
108    fn from(value: f64) -> Self {
109        Self::Float(value)
110    }
111}
112
113impl From<bool> for HostProviderConfigurationValue {
114    fn from(value: bool) -> Self {
115        Self::Bool(value)
116    }
117}
118
119impl From<Vec<HostProviderConfigurationValue>> for HostProviderConfigurationValue {
120    fn from(value: Vec<HostProviderConfigurationValue>) -> Self {
121        Self::Array(value)
122    }
123}
124
125impl From<HostProviderConfiguration> for HostProviderConfigurationValue {
126    fn from(value: HostProviderConfiguration) -> Self {
127        Self::Table(value)
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::{HostProviderConfiguration, HostProviderConfigurationValue};
134    use ecow::EcoString;
135    use std::collections::BTreeMap;
136
137    #[test]
138    fn configuration_preserves_every_owned_value_family() {
139        let nested = HostProviderConfiguration::new(BTreeMap::from([(
140            EcoString::from("enabled"),
141            true.into(),
142        )]));
143        let configuration = HostProviderConfiguration::new(BTreeMap::from([
144            (
145                EcoString::from("array"),
146                vec![1_i64.into(), false.into()].into(),
147            ),
148            (EcoString::from("float"), 1.5.into()),
149            (EcoString::from("integer"), (-7_i64).into()),
150            (EcoString::from("string"), "value".into()),
151            (EcoString::from("table"), nested.into()),
152        ]));
153
154        assert!(!configuration.is_empty());
155        assert_eq!(configuration.iter().len(), 5);
156        assert_eq!(
157            configuration
158                .get("string")
159                .and_then(|value| value.as_string()),
160            Some(&EcoString::from("value"))
161        );
162        assert_eq!(
163            configuration
164                .get("integer")
165                .and_then(HostProviderConfigurationValue::as_integer),
166            Some(-7)
167        );
168        assert_eq!(
169            configuration
170                .get("float")
171                .and_then(HostProviderConfigurationValue::as_float),
172            Some(1.5)
173        );
174        let array = configuration
175            .get("array")
176            .and_then(HostProviderConfigurationValue::as_array)
177            .expect("array should be present");
178        assert_eq!(array[0].as_integer(), Some(1));
179        assert_eq!(array[1].as_bool(), Some(false));
180        assert_eq!(
181            configuration
182                .get("table")
183                .and_then(HostProviderConfigurationValue::as_table)
184                .and_then(|table| table.get("enabled"))
185                .and_then(HostProviderConfigurationValue::as_bool),
186            Some(true)
187        );
188        assert_eq!(configuration.clone(), configuration);
189        assert!(HostProviderConfiguration::empty().is_empty());
190    }
191
192    #[test]
193    fn configuration_accessors_do_not_coerce_value_families() {
194        let value = HostProviderConfigurationValue::String("text".into());
195        let owned = HostProviderConfigurationValue::from(EcoString::from("owned"));
196
197        assert_eq!(value.as_string().map(EcoString::as_str), Some("text"));
198        assert_eq!(owned.as_string().map(EcoString::as_str), Some("owned"));
199        assert_eq!(value.as_integer(), None);
200        assert_eq!(value.as_float(), None);
201        assert_eq!(value.as_bool(), None);
202        assert_eq!(value.as_array(), None);
203        assert_eq!(value.as_table(), None);
204        assert_eq!(HostProviderConfigurationValue::Integer(1).as_string(), None);
205    }
206}