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
15pub 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 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 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}