config_maint/
value.rs

1use error::*;
2use serde::de::{Deserialize, Deserializer, Visitor};
3use std::collections::HashMap;
4use std::fmt;
5use std::fmt::Display;
6
7/// Underlying kind of the configuration value.
8#[derive(Debug, Clone, PartialEq)]
9pub enum ValueKind {
10    Nil,
11    Boolean(bool),
12    Integer(i64),
13    Float(f64),
14    String(String),
15    Table(Table),
16    Array(Array),
17}
18
19pub type Array = Vec<Value>;
20pub type Table = HashMap<String, Value>;
21
22impl Default for ValueKind {
23    fn default() -> Self {
24        ValueKind::Nil
25    }
26}
27
28impl<T> From<Option<T>> for ValueKind
29where
30    T: Into<ValueKind>,
31{
32    fn from(value: Option<T>) -> Self {
33        match value {
34            Some(value) => value.into(),
35            None => ValueKind::Nil,
36        }
37    }
38}
39
40impl From<String> for ValueKind {
41    fn from(value: String) -> Self {
42        ValueKind::String(value)
43    }
44}
45
46impl<'a> From<&'a str> for ValueKind {
47    fn from(value: &'a str) -> Self {
48        ValueKind::String(value.into())
49    }
50}
51
52impl From<i64> for ValueKind {
53    fn from(value: i64) -> Self {
54        ValueKind::Integer(value)
55    }
56}
57
58impl From<f64> for ValueKind {
59    fn from(value: f64) -> Self {
60        ValueKind::Float(value)
61    }
62}
63
64impl From<bool> for ValueKind {
65    fn from(value: bool) -> Self {
66        ValueKind::Boolean(value)
67    }
68}
69
70impl<T> From<HashMap<String, T>> for ValueKind
71where
72    T: Into<Value>,
73{
74    fn from(values: HashMap<String, T>) -> Self {
75        let mut r = HashMap::new();
76
77        for (k, v) in values {
78            r.insert(k.clone(), v.into());
79        }
80
81        ValueKind::Table(r)
82    }
83}
84
85impl<T> From<Vec<T>> for ValueKind
86where
87    T: Into<Value>,
88{
89    fn from(values: Vec<T>) -> Self {
90        let mut l = Vec::new();
91
92        for v in values {
93            l.push(v.into());
94        }
95
96        ValueKind::Array(l)
97    }
98}
99
100impl Display for ValueKind {
101    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
102        match *self {
103            ValueKind::String(ref value) => write!(f, "{}", value),
104            ValueKind::Boolean(value) => write!(f, "{}", value),
105            ValueKind::Integer(value) => write!(f, "{}", value),
106            ValueKind::Float(value) => write!(f, "{}", value),
107            ValueKind::Nil => write!(f, "nil"),
108
109            // TODO: Figure out a nice Display for these
110            ValueKind::Table(ref table) => write!(f, "{:?}", table),
111            ValueKind::Array(ref array) => write!(f, "{:?}", array),
112        }
113    }
114}
115
116/// A configuration value.
117#[derive(Default, Debug, Clone, PartialEq)]
118pub struct Value {
119    /// A description of the original location of the value.
120    ///
121    /// A Value originating from a File might contain:
122    /// ```text
123    /// Settings.toml
124    /// ```
125    ///
126    /// A Value originating from the environment would contain:
127    /// ```text
128    /// the envrionment
129    /// ```
130    ///
131    /// A Value originating from a remote source might contain:
132    /// ```text
133    /// etcd+http://127.0.0.1:2379
134    /// ```
135    origin: Option<String>,
136
137    /// Underlying kind of the configuration value.
138    pub kind: ValueKind,
139}
140
141impl Value {
142    /// Create a new value instance that will remember its source uri.
143    pub fn new<V>(origin: Option<&String>, kind: V) -> Self
144    where
145        V: Into<ValueKind>,
146    {
147        Value {
148            origin: origin.cloned(),
149            kind: kind.into(),
150        }
151    }
152
153    /// Attempt to deserialize this value into the requested type.
154    pub fn try_into<'de, T: Deserialize<'de>>(self) -> Result<T> {
155        T::deserialize(self)
156    }
157
158    /// Returns `self` as a bool, if possible.
159    // FIXME: Should this not be `try_into_*` ?
160    pub fn into_bool(self) -> Result<bool> {
161        match self.kind {
162            ValueKind::Boolean(value) => Ok(value),
163            ValueKind::Integer(value) => Ok(value != 0),
164            ValueKind::Float(value) => Ok(value != 0.0),
165
166            ValueKind::String(ref value) => {
167                match value.to_lowercase().as_ref() {
168                    "1" | "true" | "on" | "yes" => Ok(true),
169                    "0" | "false" | "off" | "no" => Ok(false),
170
171                    // Unexpected string value
172                    s => Err(ConfigError::invalid_type(
173                        self.origin.clone(),
174                        Unexpected::Str(s.into()),
175                        "a boolean",
176                    )),
177                }
178            }
179
180            // Unexpected type
181            ValueKind::Nil => Err(ConfigError::invalid_type(
182                self.origin,
183                Unexpected::Unit,
184                "a boolean",
185            )),
186            ValueKind::Table(_) => Err(ConfigError::invalid_type(
187                self.origin,
188                Unexpected::Map,
189                "a boolean",
190            )),
191            ValueKind::Array(_) => Err(ConfigError::invalid_type(
192                self.origin,
193                Unexpected::Seq,
194                "a boolean",
195            )),
196        }
197    }
198
199    /// Returns `self` into an i64, if possible.
200    // FIXME: Should this not be `try_into_*` ?
201    pub fn into_int(self) -> Result<i64> {
202        match self.kind {
203            ValueKind::Integer(value) => Ok(value),
204
205            ValueKind::String(ref s) => {
206                match s.to_lowercase().as_ref() {
207                    "true" | "on" | "yes" => Ok(1),
208                    "false" | "off" | "no" => Ok(0),
209                    _ => {
210                        s.parse().map_err(|_| {
211                            // Unexpected string
212                            ConfigError::invalid_type(
213                                self.origin.clone(),
214                                Unexpected::Str(s.clone()),
215                                "an integer",
216                            )
217                        })
218                    }
219                }
220            }
221
222            ValueKind::Boolean(value) => Ok(if value { 1 } else { 0 }),
223            ValueKind::Float(value) => Ok(value.round() as i64),
224
225            // Unexpected type
226            ValueKind::Nil => Err(ConfigError::invalid_type(
227                self.origin,
228                Unexpected::Unit,
229                "an integer",
230            )),
231            ValueKind::Table(_) => Err(ConfigError::invalid_type(
232                self.origin,
233                Unexpected::Map,
234                "an integer",
235            )),
236            ValueKind::Array(_) => Err(ConfigError::invalid_type(
237                self.origin,
238                Unexpected::Seq,
239                "an integer",
240            )),
241        }
242    }
243
244    /// Returns `self` into a f64, if possible.
245    // FIXME: Should this not be `try_into_*` ?
246    pub fn into_float(self) -> Result<f64> {
247        match self.kind {
248            ValueKind::Float(value) => Ok(value),
249
250            ValueKind::String(ref s) => {
251                match s.to_lowercase().as_ref() {
252                    "true" | "on" | "yes" => Ok(1.0),
253                    "false" | "off" | "no" => Ok(0.0),
254                    _ => {
255                        s.parse().map_err(|_| {
256                            // Unexpected string
257                            ConfigError::invalid_type(
258                                self.origin.clone(),
259                                Unexpected::Str(s.clone()),
260                                "a floating point",
261                            )
262                        })
263                    }
264                }
265            }
266
267            ValueKind::Integer(value) => Ok(value as f64),
268            ValueKind::Boolean(value) => Ok(if value { 1.0 } else { 0.0 }),
269
270            // Unexpected type
271            ValueKind::Nil => Err(ConfigError::invalid_type(
272                self.origin,
273                Unexpected::Unit,
274                "a floating point",
275            )),
276            ValueKind::Table(_) => Err(ConfigError::invalid_type(
277                self.origin,
278                Unexpected::Map,
279                "a floating point",
280            )),
281            ValueKind::Array(_) => Err(ConfigError::invalid_type(
282                self.origin,
283                Unexpected::Seq,
284                "a floating point",
285            )),
286        }
287    }
288
289    /// Returns `self` into a str, if possible.
290    // FIXME: Should this not be `try_into_*` ?
291    pub fn into_str(self) -> Result<String> {
292        match self.kind {
293            ValueKind::String(value) => Ok(value),
294
295            ValueKind::Boolean(value) => Ok(value.to_string()),
296            ValueKind::Integer(value) => Ok(value.to_string()),
297            ValueKind::Float(value) => Ok(value.to_string()),
298
299            // Cannot convert
300            ValueKind::Nil => Err(ConfigError::invalid_type(
301                self.origin,
302                Unexpected::Unit,
303                "a string",
304            )),
305            ValueKind::Table(_) => Err(ConfigError::invalid_type(
306                self.origin,
307                Unexpected::Map,
308                "a string",
309            )),
310            ValueKind::Array(_) => Err(ConfigError::invalid_type(
311                self.origin,
312                Unexpected::Seq,
313                "a string",
314            )),
315        }
316    }
317
318    /// Returns `self` into an array, if possible
319    // FIXME: Should this not be `try_into_*` ?
320    pub fn into_array(self) -> Result<Vec<Value>> {
321        match self.kind {
322            ValueKind::Array(value) => Ok(value),
323
324            // Cannot convert
325            ValueKind::Float(value) => Err(ConfigError::invalid_type(
326                self.origin,
327                Unexpected::Float(value),
328                "an array",
329            )),
330            ValueKind::String(value) => Err(ConfigError::invalid_type(
331                self.origin,
332                Unexpected::Str(value),
333                "an array",
334            )),
335            ValueKind::Integer(value) => Err(ConfigError::invalid_type(
336                self.origin,
337                Unexpected::Integer(value),
338                "an array",
339            )),
340            ValueKind::Boolean(value) => Err(ConfigError::invalid_type(
341                self.origin,
342                Unexpected::Bool(value),
343                "an array",
344            )),
345            ValueKind::Nil => Err(ConfigError::invalid_type(
346                self.origin,
347                Unexpected::Unit,
348                "an array",
349            )),
350            ValueKind::Table(_) => Err(ConfigError::invalid_type(
351                self.origin,
352                Unexpected::Map,
353                "an array",
354            )),
355        }
356    }
357
358    /// If the `Value` is a Table, returns the associated Map.
359    // FIXME: Should this not be `try_into_*` ?
360    pub fn into_table(self) -> Result<HashMap<String, Value>> {
361        match self.kind {
362            ValueKind::Table(value) => Ok(value),
363
364            // Cannot convert
365            ValueKind::Float(value) => Err(ConfigError::invalid_type(
366                self.origin,
367                Unexpected::Float(value),
368                "a map",
369            )),
370            ValueKind::String(value) => Err(ConfigError::invalid_type(
371                self.origin,
372                Unexpected::Str(value),
373                "a map",
374            )),
375            ValueKind::Integer(value) => Err(ConfigError::invalid_type(
376                self.origin,
377                Unexpected::Integer(value),
378                "a map",
379            )),
380            ValueKind::Boolean(value) => Err(ConfigError::invalid_type(
381                self.origin,
382                Unexpected::Bool(value),
383                "a map",
384            )),
385            ValueKind::Nil => Err(ConfigError::invalid_type(
386                self.origin,
387                Unexpected::Unit,
388                "a map",
389            )),
390            ValueKind::Array(_) => Err(ConfigError::invalid_type(
391                self.origin,
392                Unexpected::Seq,
393                "a map",
394            )),
395        }
396    }
397}
398
399impl<'de> Deserialize<'de> for Value {
400    #[inline]
401    fn deserialize<D>(deserializer: D) -> ::std::result::Result<Value, D::Error>
402    where
403        D: Deserializer<'de>,
404    {
405        struct ValueVisitor;
406
407        impl<'de> Visitor<'de> for ValueVisitor {
408            type Value = Value;
409
410            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
411                formatter.write_str("any valid configuration value")
412            }
413
414            #[inline]
415            fn visit_bool<E>(self, value: bool) -> ::std::result::Result<Value, E> {
416                Ok(value.into())
417            }
418
419            #[inline]
420            fn visit_i8<E>(self, value: i8) -> ::std::result::Result<Value, E> {
421                Ok((value as i64).into())
422            }
423
424            #[inline]
425            fn visit_i16<E>(self, value: i16) -> ::std::result::Result<Value, E> {
426                Ok((value as i64).into())
427            }
428
429            #[inline]
430            fn visit_i32<E>(self, value: i32) -> ::std::result::Result<Value, E> {
431                Ok((value as i64).into())
432            }
433
434            #[inline]
435            fn visit_i64<E>(self, value: i64) -> ::std::result::Result<Value, E> {
436                Ok(value.into())
437            }
438
439            #[inline]
440            fn visit_u8<E>(self, value: u8) -> ::std::result::Result<Value, E> {
441                Ok((value as i64).into())
442            }
443
444            #[inline]
445            fn visit_u16<E>(self, value: u16) -> ::std::result::Result<Value, E> {
446                Ok((value as i64).into())
447            }
448
449            #[inline]
450            fn visit_u32<E>(self, value: u32) -> ::std::result::Result<Value, E> {
451                Ok((value as i64).into())
452            }
453
454            #[inline]
455            fn visit_u64<E>(self, value: u64) -> ::std::result::Result<Value, E> {
456                // FIXME: This is bad
457                Ok((value as i64).into())
458            }
459
460            #[inline]
461            fn visit_f64<E>(self, value: f64) -> ::std::result::Result<Value, E> {
462                Ok(value.into())
463            }
464
465            #[inline]
466            fn visit_str<E>(self, value: &str) -> ::std::result::Result<Value, E>
467            where
468                E: ::serde::de::Error,
469            {
470                self.visit_string(String::from(value))
471            }
472
473            #[inline]
474            fn visit_string<E>(self, value: String) -> ::std::result::Result<Value, E> {
475                Ok(value.into())
476            }
477
478            #[inline]
479            fn visit_none<E>(self) -> ::std::result::Result<Value, E> {
480                Ok(Value::new(None, ValueKind::Nil))
481            }
482
483            #[inline]
484            fn visit_some<D>(self, deserializer: D) -> ::std::result::Result<Value, D::Error>
485            where
486                D: Deserializer<'de>,
487            {
488                Deserialize::deserialize(deserializer)
489            }
490
491            #[inline]
492            fn visit_unit<E>(self) -> ::std::result::Result<Value, E> {
493                Ok(Value::new(None, ValueKind::Nil))
494            }
495
496            #[inline]
497            fn visit_seq<V>(self, mut visitor: V) -> ::std::result::Result<Value, V::Error>
498            where
499                V: ::serde::de::SeqAccess<'de>,
500            {
501                let mut vec = Array::new();
502
503                while let Some(elem) = visitor.next_element()? {
504                    vec.push(elem);
505                }
506
507                Ok(vec.into())
508            }
509
510            fn visit_map<V>(self, mut visitor: V) -> ::std::result::Result<Value, V::Error>
511            where
512                V: ::serde::de::MapAccess<'de>,
513            {
514                let mut values = Table::new();
515
516                while let Some((key, value)) = visitor.next_entry()? {
517                    values.insert(key, value);
518                }
519
520                Ok(values.into())
521            }
522        }
523
524        deserializer.deserialize_any(ValueVisitor)
525    }
526}
527
528impl<T> From<T> for Value
529where
530    T: Into<ValueKind>,
531{
532    fn from(value: T) -> Self {
533        Value {
534            origin: None,
535            kind: value.into(),
536        }
537    }
538}
539
540impl Display for Value {
541    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
542        write!(f, "{}", self.kind)
543    }
544}