miau/configuration/
tree.rs

1use crate::{
2    configuration::{CompoundKey, ConfigurationRead, Key, Value},
3    error::{ConfigurationError, ErrorCode},
4};
5use serde::{de::DeserializeOwned, Deserialize, Serialize};
6use std::{
7    collections::HashMap,
8    convert::{TryFrom, TryInto},
9    fmt::Display,
10};
11
12/// Stores information from single configuration source.
13///
14/// It is a tree whoose nodes can be maps or arrays of other trees and leaves are values.
15///
16/// To read values from `ConfigurationTree` you need to pull [`ConfigurationRead`](super::ConfigurationRead) in scope.
17/// # Example
18///```rust
19///use miau::configuration::{Configuration, ConfigurationRead, Value, ConfigurationTree};
20///
21/// // just for demonstration purpose, you should rely on serde and Configuration to get reference to tree
22///let configuration = ConfigurationTree::Value(None);
23///
24///let word: Option<String> = configuration.get("word");
25///assert_eq!(None, word);
26///```
27#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
28#[serde(untagged)]
29pub enum ConfigurationTree {
30    /// Configuration value
31    Value(Option<Value>),
32    /// Map of other configuration tress.
33    Map(HashMap<String, ConfigurationTree>),
34    /// Array of other configuration trees.
35    Array(Vec<ConfigurationTree>),
36}
37
38/// Describes node type of current [`ConfigurationTree`].
39#[derive(Debug, Eq, PartialEq)]
40pub enum NodeType {
41    /// Node type of [`ConfigurationTree::Value`]
42    Value,
43    /// Node type of [`ConfigurationTree::Map`]
44    Map,
45    /// Node type of [`ConfigurationTree::Array`]
46    Array,
47}
48
49impl ConfigurationTree {
50    pub(crate) fn get_result_internal<'a, T>(
51        &'a self,
52        keys: &CompoundKey,
53    ) -> Result<Option<T>, ConfigurationError>
54    where
55        T: TryFrom<&'a Value, Error = ConfigurationError>,
56    {
57        self.descend_many(keys)
58            .and_then(|node| node.get_value::<T>().map_err(|e| e.enrich_with_keys(keys)))
59    }
60
61    /// Retrieves strongly typed primitive value from this ConfigurationTree.
62    ///
63    /// This operation can only be succesfull if this is a leaf of the tree.
64    /// To retrive complex structures, for instance vectors or user defined structs, use [`try_convert_into`](Self::try_convert_into).
65    pub fn get_value<'a, T>(&'a self) -> Result<Option<T>, ConfigurationError>
66    where
67        T: TryFrom<&'a Value, Error = ConfigurationError>,
68    {
69        match self {
70            ConfigurationTree::Value(Some(v)) => match TryFrom::try_from(v) {
71                Ok(t) => Ok(Some(t)),
72                Err(e) => Err(e),
73            },
74            ConfigurationTree::Value(None) => Ok(None),
75            ConfigurationTree::Array(_) => {
76                Err(ErrorCode::WrongNodeType(NodeType::Value, NodeType::Array).into())
77            }
78            ConfigurationTree::Map(_) => {
79                Err(ErrorCode::WrongNodeType(NodeType::Value, NodeType::Map).into())
80            }
81        }
82    }
83
84    /// Deserializes `ConfigurationTree` into strongly typed struct.
85    ///
86    /// It is only required that struct to be deserialized to implements [`Deserialize`](serde::Deserialize)
87    /// and contains no borrowed fields, for instance `&str`.
88    /// Due to memory model of `miau` it is impossible to deserialize into such fields.
89    pub fn try_convert_into<T: DeserializeOwned>(&'_ self) -> Result<T, ConfigurationError> {
90        T::deserialize(self).map_err(|e| {
91            e.enrich_with_context(format!(
92                "Failed to deserialize configuration to type {}",
93                std::any::type_name::<T>()
94            ))
95        })
96    }
97
98    pub(crate) fn node_type(&self) -> NodeType {
99        match self {
100            ConfigurationTree::Value(_) => NodeType::Value,
101            ConfigurationTree::Map(_) => NodeType::Map,
102            ConfigurationTree::Array(_) => NodeType::Array,
103        }
104    }
105
106    pub(crate) fn descend_many(
107        &self,
108        keys: &CompoundKey,
109    ) -> Result<&ConfigurationTree, ConfigurationError> {
110        Result::Ok(self).and_then(|n| n.descend_iter(keys.iter()))
111    }
112
113    fn descend_iter<'a>(
114        &self,
115        mut kiter: impl Iterator<Item = &'a Key>,
116    ) -> Result<&ConfigurationTree, ConfigurationError> {
117        match kiter.next() {
118            Some(key) => self
119                .descend(key)
120                .and_then(|n| n.descend_iter(kiter))
121                .map_err(|e| e.enrich_with_key(key.clone())),
122            None => Ok(self),
123        }
124    }
125
126    pub(crate) fn descend(&self, key: &Key) -> Result<&ConfigurationTree, ConfigurationError> {
127        match self {
128            ConfigurationTree::Value(_) => match key {
129                Key::Array(_) => {
130                    Err(ErrorCode::WrongNodeType(NodeType::Array, NodeType::Value).into())
131                }
132                Key::Map(_) => Err(ErrorCode::WrongNodeType(NodeType::Map, NodeType::Value).into()),
133            },
134            ConfigurationTree::Array(array) => match key {
135                Key::Array(index) => match array.get(*index) {
136                    Some(node) => Ok(node),
137                    None => Err(ErrorCode::IndexOutOfRange(*index).into()),
138                },
139                Key::Map(inner_key) => {
140                    Err(ErrorCode::WrongKeyType(NodeType::Array, inner_key.to_owned()).into())
141                }
142            },
143            ConfigurationTree::Map(map) => match key {
144                Key::Array(i) => Err(ErrorCode::WrongKeyType(NodeType::Map, i.to_string()).into()),
145                Key::Map(k) => match map.get(k) {
146                    Some(node) => Ok(node),
147                    None => Err(ErrorCode::KeyNotFound(k.to_owned()).into()),
148                },
149            },
150        }
151    }
152}
153
154impl<'config, T, K> ConfigurationRead<'config, T, K> for ConfigurationTree
155where
156    T: TryFrom<&'config Value, Error = ConfigurationError>,
157    K: TryInto<CompoundKey, Error = ConfigurationError>,
158{
159    fn get_result(&'config self, keys: K) -> Result<Option<T>, ConfigurationError> {
160        let keys = keys.try_into()?;
161        self.get_result_internal(&keys)
162    }
163}
164
165pub(crate) fn merge(
166    previous: ConfigurationTree,
167    next: ConfigurationTree,
168) -> Result<ConfigurationTree, ConfigurationError> {
169    match (previous, next) {
170        (_, vn @ ConfigurationTree::Value(_)) => Ok(vn),
171        (ConfigurationTree::Map(mp), ConfigurationTree::Map(mn)) => {
172            Ok(ConfigurationTree::Map(merge_maps(mp, mn)?))
173        }
174        (ConfigurationTree::Array(vp), ConfigurationTree::Array(vn)) => {
175            Ok(ConfigurationTree::Array(merge_arrays(vp, vn)))
176        }
177        (vp, vm) => Err(ErrorCode::BadNodeMerge(vp.node_type(), vm.node_type()).into()),
178    }
179}
180
181fn merge_maps(
182    mut previous: HashMap<String, ConfigurationTree>,
183    mut next: HashMap<String, ConfigurationTree>,
184) -> Result<HashMap<String, ConfigurationTree>, ConfigurationError> {
185    for (key, next_node) in next.drain() {
186        if !previous.contains_key(&key) {
187            previous.insert(key.clone(), next_node.clone());
188        } else {
189            let previous_node = previous.remove(&key).unwrap();
190            match (previous_node, next_node) {
191                (ConfigurationTree::Value(_), vn @ ConfigurationTree::Value(_)) => {
192                    previous.insert(key.clone(), vn.clone());
193                }
194                (ConfigurationTree::Map(mp), ConfigurationTree::Map(mn)) => {
195                    previous.insert(
196                        key.clone(),
197                        ConfigurationTree::Map(
198                            merge_maps(mp, mn)
199                                .map_err(|e| e.enrich_with_key(Key::Map(key.clone())))?,
200                        ),
201                    );
202                }
203                (ConfigurationTree::Array(vp), ConfigurationTree::Array(vn)) => {
204                    previous.insert(key.clone(), ConfigurationTree::Array(merge_arrays(vp, vn)));
205                }
206                (vp, vn) => {
207                    let error: ConfigurationError =
208                        ErrorCode::BadNodeMerge(vp.node_type(), vn.node_type()).into();
209
210                    return Err(error
211                        .enrich_with_context("Failed to merge maps")
212                        .enrich_with_key(Key::Map(key)));
213                }
214            }
215        }
216    }
217
218    Ok(previous)
219}
220
221fn merge_arrays(
222    mut vp: Vec<ConfigurationTree>,
223    vn: Vec<ConfigurationTree>,
224) -> Vec<ConfigurationTree> {
225    if vp.len() >= vn.len() {
226        for (index, root) in vn.iter().enumerate() {
227            vp[index] = root.clone();
228        }
229    } else {
230        vp.clear();
231        for e in vn.iter() {
232            vp.push(e.clone())
233        }
234    }
235
236    vp
237}
238
239impl Display for NodeType {
240    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241        match self {
242            NodeType::Value => write!(f, "value"),
243            NodeType::Map => write!(f, "map"),
244            NodeType::Array => write!(f, "array"),
245        }
246    }
247}
248
249macro_rules! try_from_for {
250    ($($t:ty),*) => {
251        $(impl TryFrom<&ConfigurationTree> for $t {
252            type Error = ConfigurationError;
253
254            fn try_from(value: &ConfigurationTree) -> Result<Self, Self::Error> {
255                match value {
256                    ConfigurationTree::Value(Some(tv)) => tv.try_into(),
257                    ConfigurationTree::Value(None) => Err(ErrorCode::NullValue.into()),
258                    ConfigurationTree::Map(_) => Err(ErrorCode::WrongNodeType(NodeType::Value, NodeType::Map).into()),
259                    ConfigurationTree::Array(_) => Err(ErrorCode::WrongNodeType(NodeType::Value, NodeType::Array).into()),
260                }
261            }
262        })*
263    };
264}
265
266try_from_for!(i8, i16, i32, i64, isize, f32, f64, bool, String);
267
268impl<'conf> TryFrom<&'conf ConfigurationTree> for &'conf str {
269    type Error = ConfigurationError;
270
271    fn try_from(value: &'conf ConfigurationTree) -> Result<Self, Self::Error> {
272        match value {
273            ConfigurationTree::Value(Some(tv)) => tv.try_into(),
274            ConfigurationTree::Value(None) => Ok(""),
275            ConfigurationTree::Map(_) => {
276                Err(ErrorCode::WrongNodeType(NodeType::Value, NodeType::Map).into())
277            }
278            ConfigurationTree::Array(_) => {
279                Err(ErrorCode::WrongNodeType(NodeType::Value, NodeType::Array).into())
280            }
281        }
282    }
283}