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#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
28#[serde(untagged)]
29pub enum ConfigurationTree {
30 Value(Option<Value>),
32 Map(HashMap<String, ConfigurationTree>),
34 Array(Vec<ConfigurationTree>),
36}
37
38#[derive(Debug, Eq, PartialEq)]
40pub enum NodeType {
41 Value,
43 Map,
45 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 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 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}