1use std::collections::HashMap;
4use std::str::FromStr;
5use std::string::ToString;
6use ::error::ConfigError;
7
8#[derive(PartialEq)]
10#[derive(Debug)]
11pub struct Config {
12 root: Value
13}
14
15pub type SettingsList = HashMap<String, Setting>;
17
18#[derive(PartialEq)]
20#[derive(Debug)]
21pub struct Setting {
22 pub name: String,
24 pub value: Value
26}
27
28#[derive(PartialEq)]
30#[derive(Debug)]
31pub enum Value {
32 Svalue(ScalarValue),
34 Array(ArrayValue),
36 List(ListValue),
39 Group(SettingsList)
41}
42
43#[derive(PartialEq)]
45#[derive(Debug)]
46pub enum ScalarValue {
47 Boolean(bool),
49 Integer32(i32),
51 Integer64(i64),
53 Floating32(f32),
55 Floating64(f64),
57 Str(String)
59}
60
61pub type ArrayValue = Vec<Value>;
64
65pub type ListValue = Vec<Value>;
68
69impl Config {
70 pub fn new(sl: SettingsList) -> Config {
72 Config { root: Value::Group(sl) }
73 }
74
75 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 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 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 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 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 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 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 pub fn lookup_boolean_or(&self, path: &str, default: bool) -> bool {
234 self.lookup_boolean(path).unwrap_or(default)
235 }
236
237 pub fn lookup_integer32_or(&self, path: &str, default: i32) -> i32 {
243 self.lookup_integer32(path).unwrap_or(default)
244 }
245
246 pub fn lookup_integer64_or(&self, path: &str, default: i64) -> i64 {
252 self.lookup_integer64(path).unwrap_or(default)
253 }
254
255 pub fn lookup_floating32_or(&self, path: &str, default: f32) -> f32 {
261 self.lookup_floating32(path).unwrap_or(default)
262 }
263
264 pub fn lookup_floating64_or(&self, path: &str, default: f64) -> f64 {
268 self.lookup_floating64(path).unwrap_or(default)
269 }
270
271 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
281impl 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 pub fn new(sname: String, val: Value) -> Setting {
350 Setting { name: sname, value: val }
351 }
352}
353
354impl 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 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 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}