1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#[macro_use]
extern crate serde_json;
use serde_json::{Value};
#[derive(Debug)]
pub struct ConfigStack {
path_sep: char,
configs: Vec<Value>,
}
#[derive(Debug, PartialEq)]
pub enum Lookup {
Missing,
Found(Value),
}
impl ConfigStack {
pub fn new() -> ConfigStack {
ConfigStack { path_sep: '/', configs: vec![], }
}
pub fn with_path_sep(self, sep: char) -> ConfigStack {
ConfigStack {
path_sep: sep,
configs: self.configs,
}
}
pub fn push(mut self, config: Value) -> ConfigStack {
self.configs.push(config);
self
}
pub fn pop(mut self) -> Option<Value> {
self.configs.pop()
}
fn path_to_parts(&self, path: &str) -> Vec<String> {
let parts = path.trim_matches(self.path_sep).split(self.path_sep);
parts.map(|s| s.to_string()).collect()
}
pub fn get(&self, path: &str) -> Lookup {
self.get_parts(self.path_to_parts(path))
}
pub fn get_parts(&self, path_parts: Vec<String>) -> Lookup {
'outer: for i in 0..self.configs.len() {
let idx = self.configs.len() - i - 1;
let mut node = &self.configs[idx];
for part in path_parts.iter() {
match node.get(part) {
Some(subobj) => node = subobj,
None => {
if idx == 0 {
return Lookup::Missing;
}
continue 'outer;
},
}
}
return Lookup::Found(node.to_owned());
}
return Lookup::Missing;
}
}
mod tests {
use super::*;
#[test]
fn test_lookup() {
fn work() -> serde_json::Result<ConfigStack> {
let stack = ConfigStack::new();
let s1 = r#"{
"a": {
"b": {
"c": 1,
"d": [1, 2, 3]
},
"e": 1
}
}"#;
let v1: Value = serde_json::from_str(s1)?;
let s2 = r#"{
"a": {
"b": {
"c": 2
}
}
}"#;
let v2: Value = serde_json::from_str(s2)?;
Ok(stack.push(v1).push(v2))
}
match work() {
Ok(stack) => {
let expected: Value = json!(2);
assert_eq!(stack.get("a/b/c"), Lookup::Found(expected));
},
_ => assert!(false),
}
}
}