1use serde_json::Value;
2
3use crate::{errors::Error, Path};
4
5pub fn walk(value: &Value, path: Path) -> Result<&Value, Error> {
6 if let Some((head, tail)) = path.split_head() {
7 match value {
8 Value::Object(map) => map
9 .get(&head)
10 .ok_or(Error::PathDoesntExist)
11 .and_then(|value| walk(value, tail)),
12 Value::Array(vec) => {
13 let index = parse_array_index(vec, head)?;
14 walk(vec.get(index).ok_or(Error::PathDoesntExist)?, tail)
15 }
16 _ => Err(Error::PathDoesntExist),
17 }
18 } else {
19 Ok(value)
20 }
21}
22
23pub fn parse_array_index<T>(vec: &[T], s: impl AsRef<str>) -> Result<usize, Error> {
24 s.as_ref().parse::<usize>().or_else(|_| {
25 if s.as_ref() == "-" {
26 Ok(vec.len())
27 } else {
28 Err(Error::InvalidPath(format!(
29 "Expected array index, got '{}'",
30 s.as_ref(),
31 )))
32 }
33 })
34}
35
36#[cfg(test)]
37mod test {
38 use serde_json::json;
39
40 use super::*;
41
42 fn default_json() -> Value {
43 json!({
44 "a": "abc",
45 "b": 123,
46 "c": true,
47 "d": null,
48 "e": [1, 2, 3],
49 "f": {
50 "a": "abc",
51 "b": 123
52 }
53 })
54 }
55
56 #[test]
57 fn should_walk_json() {
58 assert_eq!(
59 walk(&default_json(), Path::new("/a")).unwrap(),
60 &json!("abc")
61 );
62 assert_eq!(walk(&default_json(), Path::new("/b")).unwrap(), &json!(123));
63 assert_eq!(
64 walk(&default_json(), Path::new("/c")).unwrap(),
65 &json!(true)
66 );
67 assert_eq!(
68 walk(&default_json(), Path::new("/d")).unwrap(),
69 &json!(null)
70 );
71 assert_eq!(
72 walk(&default_json(), Path::new("/e")).unwrap(),
73 &json!([1, 2, 3])
74 );
75 assert_eq!(walk(&default_json(), Path::new("/e/0")).unwrap(), &json!(1));
76 assert_eq!(walk(&default_json(), Path::new("/e/2")).unwrap(), &json!(3));
77 assert_eq!(
78 walk(&default_json(), Path::new("/f")).unwrap(),
79 &json!({"a": "abc", "b": 123,})
80 );
81 assert_eq!(
82 walk(&default_json(), Path::new("/f/a")).unwrap(),
83 &json!("abc")
84 );
85 assert_eq!(
86 walk(&default_json(), Path::new("/f/b")).unwrap(),
87 &json!(123)
88 );
89 assert_eq!(
90 walk(&default_json(), Path::new("/x/z")),
91 Err(Error::PathDoesntExist)
92 );
93 assert_eq!(
94 walk(&default_json(), Path::new("/e/-")),
95 Err(Error::PathDoesntExist)
96 );
97 assert_eq!(
98 walk(&default_json(), Path::root()).unwrap(),
99 &default_json()
100 )
101 }
102}