zng_ext_config/
yaml.rs

1use super::*;
2
3impl ConfigMap for indexmap::IndexMap<ConfigKey, serde_yaml::Value> {
4    fn empty() -> Self {
5        Self::new()
6    }
7
8    fn read(mut file: WatchFile) -> io::Result<Self> {
9        file.yaml().map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
10    }
11
12    fn write(self, file: &mut WriteFile) -> io::Result<()> {
13        file.write_yaml(&self)
14    }
15
16    fn get_raw(&self, key: &ConfigKey) -> Result<Option<RawConfigValue>, Arc<dyn std::error::Error + Send + Sync>> {
17        match self.get(key) {
18            Some(e) => Ok(Some(RawConfigValue::try_from(e.clone())?)),
19            None => Ok(None),
20        }
21    }
22
23    fn set_raw(map: &mut VarModify<Self>, key: ConfigKey, value: RawConfigValue) -> Result<(), Arc<dyn std::error::Error + Send + Sync>> {
24        let value = value.try_into()?;
25        if map.get(&key) != Some(&value) {
26            map.to_mut().insert(key, value);
27        }
28        Ok(())
29    }
30
31    fn contains_key(&self, key: &ConfigKey) -> bool {
32        self.contains_key(key)
33    }
34
35    fn get<O: ConfigValue>(&self, key: &ConfigKey) -> Result<Option<O>, Arc<dyn std::error::Error + Send + Sync>> {
36        if let Some(value) = self.get(key) {
37            match serde_yaml::from_value(value.clone()) {
38                Ok(v) => Ok(Some(v)),
39                Err(e) => Err(Arc::new(e)),
40            }
41        } else {
42            Ok(None)
43        }
44    }
45
46    fn set<O: ConfigValue>(map: &mut VarModify<Self>, key: ConfigKey, value: O) -> Result<(), Arc<dyn std::error::Error + Send + Sync>> {
47        match serde_yaml::to_value(&value) {
48            Ok(value) => {
49                if map.get(&key) != Some(&value) {
50                    map.to_mut().insert(key, value);
51                }
52                Ok(())
53            }
54            Err(e) => Err(Arc::new(e)),
55        }
56    }
57
58    fn remove(map: &mut VarModify<Self>, key: &ConfigKey) {
59        if map.contains_key(key) {
60            map.to_mut().shift_remove(key);
61        }
62    }
63}
64
65/// Represents a config source that synchronizes with a YAML file.
66pub type YamlConfig = SyncConfig<indexmap::IndexMap<ConfigKey, serde_yaml::Value>>;
67
68impl TryFrom<serde_yaml::Value> for RawConfigValue {
69    type Error = YamlValueRawError;
70
71    fn try_from(value: serde_yaml::Value) -> Result<Self, Self::Error> {
72        let ok = match value {
73            serde_yaml::Value::Null => serde_json::Value::Null,
74            serde_yaml::Value::Bool(b) => serde_json::Value::Bool(b),
75            serde_yaml::Value::Number(n) => {
76                // serde_json does not implicit converts float to integer, so we try integers first here.
77                serde_json::Value::Number(if let Some(n) = n.as_i64() {
78                    n.into()
79                } else if let Some(n) = n.as_u64() {
80                    n.into()
81                } else if let Some(n) = n.as_f64() {
82                    match serde_json::Number::from_f64(n) {
83                        Some(n) => n,
84                        None => return Err(YamlValueRawError::InvalidFloat(n)),
85                    }
86                } else {
87                    unreachable!()
88                })
89            }
90            serde_yaml::Value::String(s) => serde_json::Value::String(s),
91            serde_yaml::Value::Sequence(s) => serde_json::Value::Array({
92                let mut r = Vec::with_capacity(s.len());
93                for v in s {
94                    r.push(RawConfigValue::try_from(v)?.0);
95                }
96                r
97            }),
98            serde_yaml::Value::Mapping(m) => serde_json::Value::Object({
99                let mut o = serde_json::Map::with_capacity(m.len());
100                for (key, value) in m {
101                    o.insert(yaml_map_key(key)?, RawConfigValue::try_from(value)?.0);
102                }
103                o
104            }),
105            serde_yaml::Value::Tagged(v) => RawConfigValue::try_from(v.value)?.0,
106        };
107
108        Ok(Self(ok))
109    }
110}
111impl TryFrom<RawConfigValue> for serde_yaml::Value {
112    type Error = YamlValueRawError;
113
114    fn try_from(value: RawConfigValue) -> Result<Self, Self::Error> {
115        let ok = match value.0 {
116            serde_json::Value::Null => serde_yaml::Value::Null,
117            serde_json::Value::Bool(b) => serde_yaml::Value::Bool(b),
118            serde_json::Value::Number(n) => {
119                // serde_json does not implicit converts float to integer, so we try integers first here.
120                serde_yaml::Value::Number(if let Some(f) = n.as_i64() {
121                    serde_yaml::Number::from(f)
122                } else if let Some(n) = n.as_u64() {
123                    serde_yaml::Number::from(n)
124                } else if let Some(n) = n.as_f64() {
125                    serde_yaml::Number::from(n)
126                } else {
127                    unreachable!()
128                })
129            }
130            serde_json::Value::String(s) => serde_yaml::Value::String(s),
131            serde_json::Value::Array(a) => serde_yaml::Value::Sequence({
132                let mut r = Vec::with_capacity(a.len());
133                for v in a {
134                    r.push(RawConfigValue(v).try_into()?);
135                }
136                r
137            }),
138            serde_json::Value::Object(o) => serde_yaml::Value::Mapping({
139                let mut r = serde_yaml::Mapping::with_capacity(o.len());
140                for (k, v) in o {
141                    r.insert(serde_yaml::Value::String(k), RawConfigValue(v).try_into()?);
142                }
143                r
144            }),
145        };
146        Ok(ok)
147    }
148}
149
150fn yaml_map_key(key: serde_yaml::Value) -> Result<String, YamlValueRawError> {
151    let ok = match key {
152        serde_yaml::Value::Null => String::new(),
153        serde_yaml::Value::Bool(b) => b.to_string(),
154        serde_yaml::Value::Number(n) => n.to_string(),
155        serde_yaml::Value::String(s) => s,
156        serde_yaml::Value::Sequence(_) | serde_yaml::Value::Mapping(_) | serde_yaml::Value::Tagged(_) => {
157            return Err(YamlValueRawError::InvalidMapKey);
158        }
159    };
160    Ok(ok)
161}
162
163/// Error converting serde_yaml::Value, RawConfigValue.
164#[derive(Debug, Clone, Copy)]
165pub enum YamlValueRawError {
166    /// JSON only supports finite floats.
167    InvalidFloat(f64),
168    /// JSON only supports key types that are [`fmt::Display`].
169    InvalidMapKey,
170}
171impl fmt::Display for YamlValueRawError {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        match self {
174            Self::InvalidFloat(fl) => write!(f, "json does not support float `{fl}`"),
175            Self::InvalidMapKey => write!(f, "json does not support non-display keys"),
176        }
177    }
178}
179impl std::error::Error for YamlValueRawError {}
180impl From<YamlValueRawError> for Arc<dyn std::error::Error + Send + Sync> {
181    fn from(value: YamlValueRawError) -> Self {
182        Arc::new(value)
183    }
184}