jatch/patch/
walk.rs

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}