dd_manifest_tree/
lib.rs

1#![doc = include_str!(concat!("../", env!("CARGO_PKG_README")))]
2
3use std::{
4    error::Error,
5    fmt::{Debug, Display},
6};
7
8#[cfg(feature = "json")]
9pub type JsonValue = serde_json::Value;
10#[cfg(feature = "yaml")]
11pub type YamlValue = yaml_rust2::Yaml;
12#[cfg(feature = "toml")]
13pub type TomlValue = toml::Value;
14
15/// Parse the source into the value type object
16pub fn parse_manifest<V: Value>(source: &str) -> Result<V, V::Error> {
17    V::from_string(source)
18}
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21pub struct ValueError {
22    expected: &'static str,
23    actual: &'static str,
24}
25
26impl Display for ValueError {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        write!(
29            f,
30            "Value had an unexpected type. `{}` was expected, but the actual value was `{}`",
31            self.expected, self.actual
32        )
33    }
34}
35
36impl Error for ValueError {}
37
38pub trait Value: Debug + Clone + Sized {
39    type Error: std::error::Error;
40    type MapType: Map<Value = Self>;
41
42    fn type_name(&self) -> &'static str;
43    fn as_null(&self) -> Result<(), ValueError>;
44    fn as_bool(&self) -> Result<bool, ValueError>;
45    fn as_uint(&self) -> Result<u64, ValueError>;
46    fn as_int(&self) -> Result<i64, ValueError>;
47    fn as_float(&self) -> Result<f64, ValueError>;
48    fn as_string(&self) -> Result<&str, ValueError>;
49    fn as_array(&self) -> Result<&[Self], ValueError>;
50    fn as_map(&self) -> Result<&Self::MapType, ValueError>;
51
52    fn from_string(source: &str) -> Result<Self, Self::Error>;
53}
54
55#[cfg(feature = "json")]
56impl Value for serde_json::Value {
57    type Error = serde_json::Error;
58    type MapType = serde_json::Map<String, serde_json::Value>;
59
60    fn type_name(&self) -> &'static str {
61        match self {
62            serde_json::Value::Null => "null",
63            serde_json::Value::Bool(_) => "bool",
64            serde_json::Value::Number(n) if n.is_u64() => "uint",
65            serde_json::Value::Number(n) if n.is_i64() => "int",
66            serde_json::Value::Number(n) if n.is_f64() => "float",
67            serde_json::Value::Number(_) => unreachable!(),
68            serde_json::Value::String(_) => "string",
69            serde_json::Value::Array(_) => "array",
70            serde_json::Value::Object(_) => "map",
71        }
72    }
73
74    fn as_null(&self) -> Result<(), ValueError> {
75        self.as_null().ok_or_else(|| ValueError {
76            expected: "null",
77            actual: self.type_name(),
78        })
79    }
80
81    fn as_bool(&self) -> Result<bool, ValueError> {
82        self.as_bool().ok_or_else(|| ValueError {
83            expected: "bool",
84            actual: self.type_name(),
85        })
86    }
87
88    fn as_uint(&self) -> Result<u64, ValueError> {
89        self.as_u64().ok_or_else(|| ValueError {
90            expected: "uint",
91            actual: self.type_name(),
92        })
93    }
94
95    fn as_int(&self) -> Result<i64, ValueError> {
96        self.as_i64().ok_or_else(|| ValueError {
97            expected: "int",
98            actual: self.type_name(),
99        })
100    }
101
102    fn as_float(&self) -> Result<f64, ValueError> {
103        self.as_f64().ok_or_else(|| ValueError {
104            expected: "float",
105            actual: self.type_name(),
106        })
107    }
108
109    fn as_string(&self) -> Result<&str, ValueError> {
110        self.as_str().ok_or_else(|| ValueError {
111            expected: "string",
112            actual: self.type_name(),
113        })
114    }
115
116    fn as_array(&self) -> Result<&[Self], ValueError> {
117        self.as_array()
118            .map(|v| v.as_slice())
119            .ok_or_else(|| ValueError {
120                expected: "array",
121                actual: self.type_name(),
122            })
123    }
124
125    fn as_map(&self) -> Result<&Self::MapType, ValueError> {
126        self.as_object().ok_or_else(|| ValueError {
127            expected: "map",
128            actual: self.type_name(),
129        })
130    }
131
132    fn from_string(source: &str) -> Result<Self, Self::Error> {
133        serde_json::from_str(source)
134    }
135}
136
137#[cfg(feature = "yaml")]
138impl Value for yaml_rust2::Yaml {
139    type Error = yaml_rust2::ScanError;
140    type MapType = yaml_rust2::yaml::Hash;
141
142    fn type_name(&self) -> &'static str {
143        match self {
144            yaml_rust2::Yaml::Real(_) => "float",
145            yaml_rust2::Yaml::Integer(0..) => "(u)int",
146            yaml_rust2::Yaml::Integer(..0) => "int",
147            yaml_rust2::Yaml::String(_) => "string",
148            yaml_rust2::Yaml::Boolean(_) => "bool",
149            yaml_rust2::Yaml::Array(_) => "array",
150            yaml_rust2::Yaml::Hash(_) => "map",
151            yaml_rust2::Yaml::Alias(_) => "alias",
152            yaml_rust2::Yaml::Null => "null",
153            yaml_rust2::Yaml::BadValue => "bad value",
154        }
155    }
156
157    fn as_null(&self) -> Result<(), ValueError> {
158        self.is_null().then_some(()).ok_or_else(|| ValueError {
159            expected: "null",
160            actual: self.type_name(),
161        })
162    }
163
164    fn as_bool(&self) -> Result<bool, ValueError> {
165        self.as_bool().ok_or_else(|| ValueError {
166            expected: "bool",
167            actual: self.type_name(),
168        })
169    }
170
171    fn as_uint(&self) -> Result<u64, ValueError> {
172        // If string, try parse binary int
173        if let Some(s) = self.as_str() {
174            if let Some(num_str) = s.strip_prefix("0b") {
175                if let Ok(num) = u64::from_str_radix(num_str, 2) {
176                    return Ok(num);
177                }
178            }
179        }
180
181        self.as_i64()
182            .and_then(|val| (val >= 0).then_some(val as u64))
183            .ok_or_else(|| ValueError {
184                expected: "uint",
185                actual: self.type_name(),
186            })
187    }
188
189    fn as_int(&self) -> Result<i64, ValueError> {
190        // If string, try parse binary int
191        if let Some(s) = self.as_str() {
192            if let Some(num_str) = s.strip_prefix("0b") {
193                if let Ok(num) = i64::from_str_radix(num_str, 2) {
194                    return Ok(num);
195                }
196            }
197        }
198
199        self.as_i64().ok_or_else(|| ValueError {
200            expected: "int",
201            actual: self.type_name(),
202        })
203    }
204
205    fn as_float(&self) -> Result<f64, ValueError> {
206        self.as_f64().ok_or_else(|| ValueError {
207            expected: "float",
208            actual: self.type_name(),
209        })
210    }
211
212    fn as_string(&self) -> Result<&str, ValueError> {
213        self.as_str().ok_or_else(|| ValueError {
214            expected: "string",
215            actual: self.type_name(),
216        })
217    }
218
219    fn as_array(&self) -> Result<&[Self], ValueError> {
220        self.as_vec()
221            .map(|v| v.as_slice())
222            .ok_or_else(|| ValueError {
223                expected: "array",
224                actual: self.type_name(),
225            })
226    }
227
228    fn as_map(&self) -> Result<&Self::MapType, ValueError> {
229        self.as_hash().ok_or_else(|| ValueError {
230            expected: "map",
231            actual: self.type_name(),
232        })
233    }
234
235    fn from_string(source: &str) -> Result<Self, Self::Error> {
236        Ok(yaml_rust2::YamlLoader::load_from_str(source)?.remove(0))
237    }
238}
239
240#[cfg(feature = "toml")]
241impl Value for toml::Value {
242    type Error = toml::de::Error;
243    type MapType = toml::value::Table;
244
245    fn type_name(&self) -> &'static str {
246        match self {
247            toml::Value::String(_) => "string",
248            toml::Value::Integer(0..) => "(u)int",
249            toml::Value::Integer(..0) => "int",
250            toml::Value::Float(_) => "float",
251            toml::Value::Boolean(_) => "bool",
252            toml::Value::Datetime(_) => "datetime",
253            toml::Value::Array(_) => "array",
254            toml::Value::Table(_) => "map",
255        }
256    }
257
258    fn as_null(&self) -> Result<(), ValueError> {
259        Err(ValueError {
260            expected: "null",
261            actual: self.type_name(),
262        })
263    }
264
265    fn as_bool(&self) -> Result<bool, ValueError> {
266        self.as_bool().ok_or_else(|| ValueError {
267            expected: "bool",
268            actual: self.type_name(),
269        })
270    }
271
272    fn as_uint(&self) -> Result<u64, ValueError> {
273        self.as_integer()
274            .and_then(|val| (val >= 0).then_some(val as u64))
275            .ok_or_else(|| ValueError {
276                expected: "uint",
277                actual: self.type_name(),
278            })
279    }
280
281    fn as_int(&self) -> Result<i64, ValueError> {
282        self.as_integer().ok_or_else(|| ValueError {
283            expected: "int",
284            actual: self.type_name(),
285        })
286    }
287
288    fn as_float(&self) -> Result<f64, ValueError> {
289        self.as_float().ok_or_else(|| ValueError {
290            expected: "float",
291            actual: self.type_name(),
292        })
293    }
294
295    fn as_string(&self) -> Result<&str, ValueError> {
296        self.as_str().ok_or_else(|| ValueError {
297            expected: "string",
298            actual: self.type_name(),
299        })
300    }
301
302    fn as_array(&self) -> Result<&[Self], ValueError> {
303        self.as_array()
304            .map(|v| v.as_slice())
305            .ok_or_else(|| ValueError {
306                expected: "array",
307                actual: self.type_name(),
308            })
309    }
310
311    fn as_map(&self) -> Result<&Self::MapType, ValueError> {
312        self.as_table().ok_or_else(|| ValueError {
313            expected: "map",
314            actual: self.type_name(),
315        })
316    }
317
318    fn from_string(source: &str) -> Result<Self, Self::Error> {
319        toml::from_str(source)
320    }
321}
322
323pub trait Map {
324    type Value: Value;
325
326    fn iter(&self) -> impl Iterator<Item = (&str, &Self::Value)>;
327    fn get(&self, key: &str) -> Option<&Self::Value>;
328    fn contains_key(&self, key: &str) -> bool;
329}
330
331#[cfg(feature = "json")]
332impl Map for serde_json::Map<String, serde_json::Value> {
333    type Value = serde_json::Value;
334
335    fn iter(&self) -> impl Iterator<Item = (&str, &Self::Value)> {
336        self.iter().map(|(k, v)| (k.as_str(), v))
337    }
338
339    fn get(&self, key: &str) -> Option<&Self::Value> {
340        self.get(key)
341    }
342
343    fn contains_key(&self, key: &str) -> bool {
344        self.contains_key(key)
345    }
346}
347
348#[cfg(feature = "yaml")]
349impl Map for yaml_rust2::yaml::Hash {
350    type Value = yaml_rust2::Yaml;
351
352    fn iter(&self) -> impl Iterator<Item = (&str, &Self::Value)> {
353        self.iter().map(|(k, v)| (k.as_str().unwrap(), v))
354    }
355
356    fn get(&self, key: &str) -> Option<&Self::Value> {
357        self.get(&yaml_rust2::Yaml::String(key.into()))
358    }
359
360    fn contains_key(&self, key: &str) -> bool {
361        self.contains_key(&yaml_rust2::Yaml::String(key.into()))
362    }
363}
364
365#[cfg(feature = "toml")]
366impl Map for toml::Table {
367    type Value = toml::Value;
368
369    fn iter(&self) -> impl Iterator<Item = (&str, &Self::Value)> {
370        self.iter().map(|(k, v)| (k.as_str(), v))
371    }
372
373    fn get(&self, key: &str) -> Option<&Self::Value> {
374        self.get(key)
375    }
376
377    fn contains_key(&self, key: &str) -> bool {
378        self.contains_key(key)
379    }
380}