libconfig/
types.rs

1//! Internal types used to represent a configuration and corresponding primitives to browse it
2
3use std::collections::HashMap;
4use std::str::FromStr;
5use std::string::ToString;
6use ::error::ConfigError;
7
8/// The top-level `Config` type that represents a configuration
9#[derive(PartialEq)]
10#[derive(Debug)]
11pub struct Config {
12    root: Value
13}
14
15/// Settings list representation. Associates settings to their names.
16pub type SettingsList = HashMap<String, Setting>;
17
18/// A `Setting` representation. Settings have a name and a value.
19#[derive(PartialEq)]
20#[derive(Debug)]
21pub struct Setting {
22    /// Setting name, as read from the configuration file
23    pub name: String,
24    /// This setting's value. A value can be a scalar, an array, a list, or a group.
25    pub value: Value
26}
27
28/// A type representing a generic value. `Setting`s store `Value`s.
29#[derive(PartialEq)]
30#[derive(Debug)]
31pub enum Value {
32    /// A scalar
33    Svalue(ScalarValue),
34    /// An array
35    Array(ArrayValue),
36    /// A list. Arrays can only store scalars of the same type, whereas lists store `Value`s of
37    /// possibly different types, including other lists.
38    List(ListValue),
39    /// A group. Basically, a group acts as another configuration file - it stores a `SettingsList`.
40    Group(SettingsList)
41}
42
43/// The scalar values representation. Scalar values bind directly to Rust primitive types.
44#[derive(PartialEq)]
45#[derive(Debug)]
46pub enum ScalarValue {
47    /// A boolean scalar
48    Boolean(bool),
49    /// An i32 scalar
50    Integer32(i32),
51    /// An i64 scalar
52    Integer64(i64),
53    /// An f32 scalar
54    Floating32(f32),
55    /// An f64 scalar
56    Floating64(f64),
57    /// A string scalar
58    Str(String)
59}
60
61/// The type used to represent the scalars inside an array.
62/// An array can only store scalar values of the same type.
63pub type ArrayValue = Vec<Value>;
64
65/// The type used to represent the generic values inside a list.
66/// Lists are heterogeneous and can store any type of value, including other lists.
67pub type ListValue = Vec<Value>;
68
69impl Config {
70    /// Creates a new wrapper `Config` to hold a `SettingsList`
71    pub fn new(sl: SettingsList) -> Config {
72        Config { root: Value::Group(sl) }
73    }
74
75    /// Looks up a value in a configuration. A path is a dot-separated list of settings
76    /// describing the path of the desired value in the configuration.
77    /// Returns `None` if the path is invalid. A path is invalid if it is not syntactically
78    /// well-formed, if it attempts to index an array or list beyond the limit, or if it
79    /// includes an unknown setting.
80    /// # Examples
81    /// Suppose we have loaded a configuration that consists of:
82    ///
83    /// ```text
84    /// my_string = "hello";
85    /// a_list = ([1, 2, 3], true, { x = 4; }, "good bye");
86    /// ```
87    ///
88    /// Then, the path to retrieve `"hello"` is `my_string`.
89    /// The path to retrieve `true` inside `a_list` would be `a_list.[1]`.
90    /// The path to retrieve the setting `x` inside `a_list` would be `alist.[2].x`.
91    ///
92    /// Here's a small demonstration:
93    ///
94    /// ```
95    /// use config::reader::from_str;
96    ///
97    /// let my_conf = from_str("my_string = \"hello\"; a_list = ([1, 2, 3], true, { x = 4; }, \"good_bye\");").unwrap();
98    ///
99    /// let my_str_value = my_conf.lookup("my_string");
100    /// assert!(my_str_value.is_some());
101    ///
102    /// let my_boolean_value = my_conf.lookup("a_list.[1]");
103    /// assert!(my_boolean_value.is_some());
104    ///
105    /// let my_x_setting = my_conf.lookup("a_list.[2].x");
106    /// assert!(my_x_setting.is_some());
107    ///
108    /// ```
109    ///
110    pub fn lookup(&self, path: &str) -> Option<&Value> {
111        let mut last_value = &self.root;
112        for segment in path.split(".") {
113            if segment.starts_with("[") {
114                if !segment.ends_with("]") || segment.len() < 3 {
115                    return None;
116                }
117                if let Ok(index) = (&segment[1..segment.len()-1]).parse::<usize>() {
118                    if let &Value::Array(ref arr) = last_value {
119                        if index >= arr.len() {
120                            return None;
121                        }
122                        last_value = &arr[index];
123                    }
124                    else if let &Value::List(ref list) = last_value {
125                        if index >= list.len() {
126                            return None;
127                        }
128                        last_value = &list[index];
129                    } else {
130                        return None;
131                    }
132                } else {
133                    return None;
134                }
135            } else {
136                if let &Value::Group(ref settings_list) = last_value {
137                    let next_setting = match settings_list.get(&segment[..]) {
138                        Some(v) => v,
139                        None => return None
140                    };
141                    last_value = &next_setting.value;
142                } else {
143                    return None;
144                }
145            }
146        }
147        Some(last_value)
148    }
149
150    /// A convenient wrapper around `lookup()` that unwraps the underlying primitive
151    /// type of a generic `Value`.
152    ///
153    /// Returns `None` in the same way `lookup()` does; or if the underlying `Value`
154    /// type does not match with the requested type - in this case, `bool`.
155    pub fn lookup_boolean(&self, path: &str) -> Option<bool> {
156        self.lookup(path).and_then(|v|
157                                   match v {
158                                       &Value::Svalue(ScalarValue::Boolean(b)) => Some(b),
159                                       _ => None
160                                   })
161    }
162
163    /// A convenient wrapper around `lookup()` that unwraps the underlying primitive
164    /// type of a generic `Value`.
165    ///
166    /// Returns `None` in the same way `lookup()` does; or if the underlying `Value`
167    /// type does not match with the requested type - in this case, `i32`.
168    pub fn lookup_integer32(&self, path: &str) -> Option<i32> {
169        self.lookup(path).and_then(|v|
170                                   match v {
171                                       &Value::Svalue(ScalarValue::Integer32(x)) => Some(x),
172                                       _ => None
173                                   })
174    }
175
176    /// A convenient wrapper around `lookup()` that unwraps the underlying primitive
177    /// type of a generic `Value`.
178    ///
179    /// Returns `None` in the same way `lookup()` does; or if the underlying `Value`
180    /// type does not match with the requested type - in this case, `i64`.
181    pub fn lookup_integer64(&self, path: &str) -> Option<i64> {
182        self.lookup(path).and_then(|v|
183                                   match v {
184                                       &Value::Svalue(ScalarValue::Integer64(x)) => Some(x),
185                                       _ => None
186                                   })
187    }
188
189    /// A convenient wrapper around `lookup()` that unwraps the underlying primitive
190    /// type of a generic `Value`.
191    ///
192    /// Returns `None` in the same way `lookup()` does; or if the underlying `Value`
193    /// type does not match with the requested type - in this case, `f32`.
194    pub fn lookup_floating32(&self, path: &str) -> Option<f32> {
195        self.lookup(path).and_then(|v|
196                                   match v {
197                                       &Value::Svalue(ScalarValue::Floating32(x)) => Some(x),
198                                       _ => None
199                                   })
200    }
201
202    /// A convenient wrapper around `lookup()` that unwraps the underlying primitive
203    /// type of a generic `Value`.
204    ///
205    /// Returns `None` in the same way `lookup()` does; or if the underlying `Value`
206    /// type does not match with the requested type - in this case, `f64`.
207    pub fn lookup_floating64(&self, path: &str) -> Option<f64> {
208        self.lookup(path).and_then(|v|
209                                   match v {
210                                       &Value::Svalue(ScalarValue::Floating64(x)) => Some(x),
211                                       _ => None
212                                   })
213    }
214
215    /// A convenient wrapper around `lookup()` that unwraps the underlying primitive
216    /// type of a generic `Value`.
217    ///
218    /// Returns `None` in the same way `lookup()` does; or if the underlying `Value`
219    /// type does not match with the requested type - in this case, `String`.
220    pub fn lookup_str(&self, path: &str) -> Option<&str> {
221        self.lookup(path).and_then(|v|
222                                   match v {
223                                       &Value::Svalue(ScalarValue::Str(ref s)) => Some(&s[..]),
224                                       _ => None
225                                   })
226    }
227
228    /// A convenient wrapper around `lookup_boolean()` that unwraps the underlying primitive
229    /// type of a boolean `Value`.
230    ///
231    /// If either of `lookup_boolean()` or `lookup` return `None`,
232    /// then the user-provided default value is returned.
233    pub fn lookup_boolean_or(&self, path: &str, default: bool) -> bool {
234        self.lookup_boolean(path).unwrap_or(default)
235    }
236
237    /// A convenient wrapper around `lookup_integer32()` that unwraps the underlying primitive
238    /// type of an integer32 `Value`.
239    ///
240    /// If either of `lookup_integer32()` or `lookup` return `None`,
241    /// then the user-provided default value is returned.
242    pub fn lookup_integer32_or(&self, path: &str, default: i32) -> i32 {
243        self.lookup_integer32(path).unwrap_or(default)
244    }
245
246    /// A convenient wrapper around `lookup_integer64()` that unwraps the underlying primitive
247    /// type of an integer64 `Value`.
248    ///
249    /// If either of `lookup_integer64()` or `lookup` return `None`,
250    /// then the user-provided default value is returned.
251    pub fn lookup_integer64_or(&self, path: &str, default: i64) -> i64 {
252        self.lookup_integer64(path).unwrap_or(default)
253    }
254
255    /// A convenient wrapper around `lookup_floating32()` that unwraps the underlying primitive
256    /// type of an floating32 `Value`.
257    ///
258    /// If either of `lookup_floating32()` or `lookup` return `None`,
259    /// then the user-provided default value is returned.
260    pub fn lookup_floating32_or(&self, path: &str, default: f32) -> f32 {
261        self.lookup_floating32(path).unwrap_or(default)
262    }
263
264    /// A convenient wrapper around `lookup_floating64()` that unwraps the underlying primitive
265    /// type of an floating64 `Value`. If either of `lookup_floating64()` or `lookup` return `None`,
266    /// then the user-provided default value is returned.
267    pub fn lookup_floating64_or(&self, path: &str, default: f64) -> f64 {
268        self.lookup_floating64(path).unwrap_or(default)
269    }
270
271    /// A convenient wrapper around `lookup_str()` that unwraps the underlying primitive
272    /// type of a string `Value`.
273    ///
274    /// If either of `lookup_str()` or `lookup` return `None`,
275    /// then the user-provided default value is returned.
276    pub fn lookup_str_or<'a>(&'a self, path: &str, default: &'a str) -> &'a str {
277        self.lookup_str(path).unwrap_or(default)
278    }
279}
280
281// Implement `FromStr` for `Config` so it can be constructed using `parse()` method
282// on the string slice.
283impl FromStr for Config {
284    type Err = ConfigError;
285
286    fn from_str(s: &str) -> Result<Self, Self::Err> {
287        use ::reader;
288
289        reader::from_str(s)
290    }
291}
292
293impl Setting {
294    /// Creates a new setting with a given name and value
295    /// # Examples 
296    /// Let's say we want to create a setting to store an `i32`.
297    /// We start by creating a `ScalarValue`:
298    ///
299    /// ```
300    /// use config::types::ScalarValue;
301    /// # use config::types::Value;
302    /// # use config::types::Setting;
303    ///
304    /// let setting_scalarvalue = ScalarValue::Integer32(1);
305    /// # let setting_value = Value::Svalue(setting_scalarvalue);
306    /// # let setting_name = "my_setting".to_string();
307    /// # let my_setting = Setting::new(setting_name, setting_value);
308    /// ```
309    ///
310    /// Then, we wrap it into a `Value`, because settings store generic values:
311    ///
312    /// ```
313    /// # use config::types::ScalarValue;
314    /// use config::types::Value;
315    /// # use config::types::Setting;
316    ///
317    /// # let setting_scalarvalue = ScalarValue::Integer32(1);
318    /// let setting_value = Value::Svalue(setting_scalarvalue);
319    /// # let setting_name = "my_setting".to_string();
320    /// # let my_setting = Setting::new(setting_name, setting_value);
321    /// ```
322    ///
323    /// And then we choose a name for our setting and create it:
324    ///
325    /// ```
326    /// # use config::types::ScalarValue;
327    /// # use config::types::Value;
328    /// use config::types::Setting;
329    ///
330    /// # let setting_scalarvalue = ScalarValue::Integer32(1);
331    /// # let setting_value = Value::Svalue(setting_scalarvalue);
332    /// let setting_name = "my_setting".to_string();
333    /// let my_setting = Setting::new(setting_name, setting_value);
334    /// ```
335    ///
336    /// Here's the complete example:
337    ///
338    /// ```
339    /// use config::types::ScalarValue;
340    /// use config::types::Value;
341    /// use config::types::Setting;
342    ///
343    /// let setting_scalarvalue = ScalarValue::Integer32(1);
344    /// let setting_value = Value::Svalue(setting_scalarvalue);
345    /// let setting_name = "my_setting".to_string();
346    /// let my_setting = Setting::new(setting_name, setting_value);
347    /// ```
348    ///
349    pub fn new(sname: String, val: Value) -> Setting {
350        Setting { name: sname, value: val }
351    }
352}
353
354// Implement to_string() method for scalar value
355impl ToString for ScalarValue {
356    fn to_string(&self) -> String {
357        match self {
358            &ScalarValue::Boolean(ref value) => value.to_string(),
359            &ScalarValue::Integer32(ref value) => value.to_string(),
360            &ScalarValue::Integer64(ref value) => value.to_string(),
361            &ScalarValue::Floating32(ref value) => value.to_string(),
362            &ScalarValue::Floating64(ref value) => value.to_string(),
363            &ScalarValue::Str(ref value) => value.clone(),
364        }
365    }
366}
367
368#[cfg(test)]
369mod test {
370    use super::Config;
371    use types::{Value, ScalarValue, SettingsList, Setting};
372
373    #[test]
374    fn simple_lookup_generic_bool() {
375
376        let mut my_settings = SettingsList::new();
377        my_settings.insert("windows".to_string(),
378                           Setting::new("windows".to_string(),
379                                        Value::Svalue(ScalarValue::Boolean(false))));
380        my_settings.insert("linux".to_string(),
381                           Setting::new("linux".to_string(),
382                                        Value::Svalue(ScalarValue::Boolean(true))));
383        my_settings.insert("UNIX".to_string(),
384                           Setting::new("UNIX".to_string(),
385                                        Value::Svalue(ScalarValue::Boolean(false))));
386
387        let my_conf = Config::new(my_settings);
388
389        let windows_lookup = my_conf.lookup("windows");
390        assert!(windows_lookup.is_some());
391        assert_eq!(windows_lookup.unwrap(), &Value::Svalue(ScalarValue::Boolean(false)));
392
393        let linux_lookup = my_conf.lookup("linux");
394        assert!(linux_lookup.is_some());
395        assert_eq!(linux_lookup.unwrap(), &Value::Svalue(ScalarValue::Boolean(true)));
396
397        let unix_lookup = my_conf.lookup("UNIX");
398        assert!(unix_lookup.is_some());
399        assert_eq!(unix_lookup.unwrap(), &Value::Svalue(ScalarValue::Boolean(false)));
400
401    }
402
403    #[test]
404    fn simple_lookup_bool() {
405        let mut my_settings = SettingsList::new();
406        my_settings.insert("windows".to_string(),
407                           Setting::new("windows".to_string(),
408                                        Value::Svalue(ScalarValue::Boolean(false))));
409        my_settings.insert("linux".to_string(),
410                           Setting::new("linux".to_string(),
411                                        Value::Svalue(ScalarValue::Boolean(true))));
412        my_settings.insert("UNIX".to_string(),
413                           Setting::new("UNIX".to_string(),
414                                        Value::Svalue(ScalarValue::Boolean(false))));
415
416        let my_conf = Config::new(my_settings);
417
418        let windows_lookup = my_conf.lookup_boolean("windows");
419        assert!(windows_lookup.is_some());
420        assert_eq!(windows_lookup.unwrap(), false);
421
422        let linux_lookup = my_conf.lookup_boolean("linux");
423        assert!(linux_lookup.is_some());
424        assert_eq!(linux_lookup.unwrap(), true);
425
426        let unix_lookup = my_conf.lookup_boolean("UNIX");
427        assert!(unix_lookup.is_some());
428        assert_eq!(unix_lookup.unwrap(), false);
429    }
430
431    #[test]
432    fn simple_lookup_generic_integer32() {
433
434        let mut my_settings = SettingsList::new();
435        my_settings.insert("miles".to_string(),
436                           Setting::new("miles".to_string(),
437                                        Value::Svalue(ScalarValue::Integer32(3))));
438        my_settings.insert("mpg".to_string(),
439                           Setting::new("mpg".to_string(),
440                                        Value::Svalue(ScalarValue::Integer32(27))));
441
442        let my_conf = Config::new(my_settings);
443
444        let miles_lookup = my_conf.lookup("miles");
445        assert!(miles_lookup.is_some());
446        assert_eq!(miles_lookup.unwrap(), &Value::Svalue(ScalarValue::Integer32(3)));
447
448        let mpg_lookup = my_conf.lookup("mpg");
449        assert!(mpg_lookup.is_some());
450        assert_eq!(mpg_lookup.unwrap(), &Value::Svalue(ScalarValue::Integer32(27)));
451    }
452
453    #[test]
454    fn simple_lookup_integer32() {
455
456        let mut my_settings = SettingsList::new();
457        my_settings.insert("miles".to_string(),
458                           Setting::new("miles".to_string(),
459                                        Value::Svalue(ScalarValue::Integer32(3))));
460        my_settings.insert("mpg".to_string(),
461                           Setting::new("mpg".to_string(),
462                                        Value::Svalue(ScalarValue::Integer32(27))));
463
464        let my_conf = Config::new(my_settings);
465
466        let miles_lookup = my_conf.lookup_integer32("miles");
467        assert!(miles_lookup.is_some());
468        assert_eq!(miles_lookup.unwrap(), 3);
469
470        let mpg_lookup = my_conf.lookup_integer32("mpg");
471        assert!(mpg_lookup.is_some());
472        assert_eq!(mpg_lookup.unwrap(), 27);
473    }
474
475    #[test]
476    fn simple_lookup_default() {
477
478        let mut my_settings = SettingsList::new();
479        my_settings.insert("miles".to_string(),
480                           Setting::new("miles".to_string(),
481                                        Value::Svalue(ScalarValue::Integer32(3))));
482        my_settings.insert("mpg".to_string(),
483                           Setting::new("mpg".to_string(),
484                                        Value::Svalue(ScalarValue::Integer32(27))));
485
486        let my_conf = Config::new(my_settings);
487
488        let miles = my_conf.lookup_integer32_or("miles", 4);
489        assert_eq!(miles, 3);
490
491        let invalid_lookup = my_conf.lookup_integer32_or("blablabla", 22);
492        assert_eq!(invalid_lookup, 22);
493    }
494
495    #[test]
496    fn lookup_nested_empty_list() {
497        // ((()));
498        let mut my_settings = SettingsList::new();
499        my_settings.insert("list".to_string(),
500                           Setting::new("list".to_string(),
501                                        Value::List(vec![
502                                            Value::List(vec![
503                                                Value::List(Vec::new())])])));
504
505        let my_conf = Config::new(my_settings);
506
507        let first = my_conf.lookup("list.[0]");
508        assert!(first.is_some());
509        assert_eq!(first.unwrap(), &Value::List(vec![Value::List(Vec::new())]));
510
511        let second = my_conf.lookup("list.[0].[0]");
512        assert!(second.is_some());
513        assert_eq!(second.unwrap(), &Value::List(Vec::new()));
514    }
515
516    #[test]
517    fn lookup_scalar_list() {
518
519        let mut my_settings = SettingsList::new();
520        my_settings.insert("my_list".to_string(),
521                        Setting::new("my_list".to_string(),
522                                     Value::List(vec![
523                                         Value::Svalue(
524                                             ScalarValue::Str("a \"string\" with \nquo\ttes"
525                                                              .to_string())),
526                                         Value::Svalue(
527                                             ScalarValue::Integer64(9000000000000000000i64))])));
528
529        let my_conf = Config::new(my_settings);
530
531        let the_string = my_conf.lookup("my_list.[0]");
532        assert!(the_string.is_some());
533        assert_eq!(the_string.unwrap(), &Value::Svalue(ScalarValue::Str(
534            "a \"string\" with \nquo\ttes".to_string())));
535
536        let big_int = my_conf.lookup("my_list.[1]");
537        assert!(big_int.is_some());
538        assert_eq!(big_int.unwrap(), &Value::Svalue(ScalarValue::Integer64(9000000000000000000i64)));
539
540    }
541
542    #[test]
543    fn lookup_array() {
544        let mut my_settings = SettingsList::new();
545        my_settings.insert("my_array".to_string(),
546                           Setting::new("my_array".to_string(),
547                                        Value::Array(vec![
548                                            Value::Svalue(ScalarValue::Boolean(true)),
549                                            Value::Svalue(ScalarValue::Boolean(false)),
550                                            Value::Svalue(ScalarValue::Boolean(true))])));
551
552        let my_conf = Config::new(my_settings);
553
554        let value0 = my_conf.lookup("my_array.[0]");
555        assert!(value0.is_some());
556        assert_eq!(value0.unwrap(), &Value::Svalue(ScalarValue::Boolean(true)));
557
558        let value1 = my_conf.lookup("my_array.[1]");
559        assert!(value1.is_some());
560        assert_eq!(value1.unwrap(), &Value::Svalue(ScalarValue::Boolean(false)));
561
562        let value2 = my_conf.lookup("my_array.[2]");
563        assert!(value2.is_some());
564        assert_eq!(value2.unwrap(), &Value::Svalue(ScalarValue::Boolean(true)));
565    }
566
567    #[test]
568    fn lookup_values_list() {
569
570        /* my_superb_list = (
571         *     [yes, no],
572         *     21,
573         *     [0.25, .5, .125],
574         *     (()),
575         *     (("a")),
576         *     ("a"),
577         *     ["\"x\""],
578         *     (
579         *         14,
580         *         ["x"],
581         *         (
582         *             true,
583         *             (
584         *                 false,
585         *                 (
586         *                     4
587         *                 ),
588         *                 [5, 6]
589         *             ),
590         *             "y"
591         *         )
592         *    ),
593         *    "goodbye!\r\n",
594         *    {
595         *        s = [1, 2];
596         *        x = "str";
597         *        y = ();
598         *    }
599         * )
600         */
601
602       let mut group_in_list = SettingsList::new();
603        group_in_list.insert("s".to_string(),
604                             Setting::new("s".to_string(),
605                                          Value::Array(vec![
606                                              Value::Svalue(ScalarValue::Integer32(1)),
607                                              Value::Svalue(ScalarValue::Integer32(2))])));
608        group_in_list.insert("x".to_string(),
609                             Setting::new("x".to_string(),
610                                          Value::Svalue(ScalarValue::Str("str".to_string()))));
611
612        group_in_list.insert("y".to_string(),
613                             Setting::new("y".to_string(), Value::List(Vec::new())));
614
615
616        let list_elements = vec![
617            Value::Array(vec![
618                Value::Svalue(ScalarValue::Boolean(true)),
619                Value::Svalue(ScalarValue::Boolean(false))]),
620            Value::Svalue(ScalarValue::Integer32(21)),
621            Value::Array(vec![
622                Value::Svalue(ScalarValue::Floating32(0.25)),
623                Value::Svalue(ScalarValue::Floating32(0.5)),
624                Value::Svalue(ScalarValue::Floating32(0.125))]),
625            Value::List(vec![Value::List(Vec::new())]),
626            Value::List(vec![Value::List(vec![Value::Svalue(ScalarValue::Str("a".to_string()))])]),
627            Value::List(vec![Value::Svalue(ScalarValue::Str("a".to_string()))]),
628            Value::Array(vec![Value::Svalue(ScalarValue::Str("\"x\"".to_string()))]),
629            Value::List(vec![Value::Svalue(ScalarValue::Integer32(14)),
630                             Value::Array(vec![Value::Svalue(ScalarValue::Str("x".to_string()))]),
631                             Value::List(vec![Value::Svalue(ScalarValue::Boolean(true)),
632                                              Value::List(vec![
633                                                  Value::Svalue(ScalarValue::Boolean(false)),
634                                                  Value::List(vec![
635                                                      Value::Svalue(ScalarValue::Integer32(4))]),
636                                                  Value::Array(vec![
637                                                      Value::Svalue(ScalarValue::Integer32(5)),
638                                                      Value::Svalue(ScalarValue::Integer32(6))])]),
639                                              Value::Svalue(ScalarValue::Str("y".to_string()))])]),
640            Value::Svalue(ScalarValue::Str("goodbye!\r\n".to_string())),
641            Value::Group(group_in_list)];
642
643        let mut my_settings = SettingsList::new();
644        my_settings.insert("my_superb_list".to_string(),
645                           Setting::new("my_superb_list".to_string(), Value::List(list_elements)));
646
647        let my_conf = Config::new(my_settings);
648
649        let lookup_bool = my_conf.lookup("my_superb_list.[0].[1]");
650        assert!(lookup_bool.is_some());
651        assert_eq!(lookup_bool.unwrap(), &Value::Svalue(ScalarValue::Boolean(false)));
652
653        let lookup_empty_lst = my_conf.lookup("my_superb_list.[3].[0]");
654        assert!(lookup_empty_lst.is_some());
655        assert_eq!(lookup_empty_lst.unwrap(), &Value::List(Vec::new()));
656
657        let lookup_deep = my_conf.lookup("my_superb_list.[7].[2].[1].[2].[1]");
658        assert!(lookup_deep.is_some());
659        assert_eq!(lookup_deep.unwrap(), &Value::Svalue(ScalarValue::Integer32(6)));
660
661        let lookup_str = my_conf.lookup("my_superb_list.[9].x");
662        assert!(lookup_str.is_some());
663        assert_eq!(lookup_str.unwrap(), &Value::Svalue(ScalarValue::Str("str".to_string())));
664
665        let lookup_deep_int = my_conf.lookup("my_superb_list.[9].s.[1]");
666        assert!(lookup_deep_int.is_some());
667        assert_eq!(lookup_deep_int.unwrap(), &Value::Svalue(ScalarValue::Integer32(2)));
668
669        let lookup_empty_lst = my_conf.lookup("my_superb_list.[9].y");
670        assert!(lookup_empty_lst.is_some());
671        assert_eq!(lookup_empty_lst.unwrap(), &Value::List(Vec::new()));
672    }
673
674    #[test]
675    fn lookup_invalid_path() {
676        let mut my_settings = SettingsList::new();
677        my_settings.insert("my_array".to_string(),
678                           Setting::new("my_array".to_string(),
679                                        Value::Array(vec![
680                                            Value::Svalue(ScalarValue::Boolean(true)),
681                                            Value::Svalue(ScalarValue::Boolean(false)),
682                                            Value::Svalue(ScalarValue::Boolean(true))])));
683
684        let my_conf = Config::new(my_settings);
685
686        let value0 = my_conf.lookup("my_array.[1456]");
687        assert!(value0.is_none());
688
689        let value1 = my_conf.lookup("my_array.[-30]");
690        assert!(value1.is_none());
691
692        let value2 = my_conf.lookup("something_that_does_not_exist.[14].lala.lele.[24]");
693        assert!(value2.is_none());
694    }
695
696    #[test]
697    fn lookup_invalid_type() {
698        let mut my_settings = SettingsList::new();
699        my_settings.insert("my_array".to_string(),
700                           Setting::new("my_array".to_string(),
701                                        Value::Array(vec![
702                                            Value::Svalue(ScalarValue::Boolean(true)),
703                                            Value::Svalue(ScalarValue::Boolean(false)),
704                                            Value::Svalue(ScalarValue::Boolean(true))])));
705
706        let my_conf = Config::new(my_settings);
707
708        let value0 = my_conf.lookup_integer32("my_array.[0]");
709        assert!(value0.is_none());
710
711        let value1 = my_conf.lookup_str("my_array.[1]");
712        assert!(value1.is_none());
713    }
714
715    #[test]
716    fn scalar_value_to_string() {
717        let mut value = ScalarValue::Boolean(true);
718        assert_eq!(value.to_string(), "true");
719
720        value = ScalarValue::Integer32(32i32);
721        assert_eq!(value.to_string(), "32");
722
723        value = ScalarValue::Integer64(-64i64);
724        assert_eq!(value.to_string(), "-64");
725
726        value = ScalarValue::Floating32(3f32);
727        assert!(value.to_string().starts_with("3"));
728
729        value = ScalarValue::Floating64(99f64);
730        assert!(value.to_string().starts_with("99"));
731
732        value = ScalarValue::Str("this is a string".to_string());
733        assert_eq!(value.to_string(), "this is a string");
734    }
735
736    #[test]
737    fn parse_config_from_str_slice() {
738        let config: Config = "answer=42;".parse().unwrap();
739        assert!(config.lookup_integer32("answer").is_some());
740        assert_eq!(config.lookup_integer32("answer").unwrap().to_string(), "42".to_string());
741    }
742}