analyse_json/json/
paths.rs

1use super::serde_json::value::Index;
2use super::{IndexMap, Value, ValueType};
3
4/// Wrapper around [`Value`] keeping track of its location within the root parent JSON
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct ValuePath<'a> {
7    pub value: &'a Value,
8    pub path: Vec<String>, // TODO: Switch to Vec<Index> ?
9}
10
11impl<'a> ValuePath<'a> {
12    /// Returns a [`ValuePath`] (Serde JSON [`Value`] wrapper) useful for tracking the location
13    /// of indexes into the JSON
14    ///
15    /// # Arguments
16    /// * `value` - Serde JSON Value
17    /// * `path` - Vec of Strings, components of the JSON path to self.value from the root ($)
18    ///
19    /// # Examples
20    ///
21    /// ```
22    /// use analyse_json::json::paths::ValuePath;
23    ///
24    /// let value = serde_json::json!({"key": "value"});
25    /// let vp = ValuePath::new(&value, None);
26    /// ```
27    pub fn new(value: &'a Value, path: Option<Vec<String>>) -> ValuePath<'a> {
28        let path = path.unwrap_or_default();
29        ValuePath { value, path }
30    }
31
32    /// JSONpath of `value`s location within the root parent JSON
33    pub fn jsonpath(&self) -> String {
34        let mut jsonpath = String::from("$");
35        for part in &self.path {
36            if part.starts_with('[') {
37                jsonpath.push_str(part);
38            } else {
39                jsonpath.push('.');
40                jsonpath.push_str(part);
41            }
42        }
43        jsonpath
44    }
45
46    // Work around until indexing via a trait is supported
47    // https://github.com/rust-lang/rfcs/issues/997
48    /// Index into the inner value, tracks the jsonpath location
49    pub fn index(&self, index: impl JSONPathIndex) -> ValuePath<'a> {
50        let mut child_path = self.path.to_vec();
51        child_path.push(index.jsonpath());
52        ValuePath {
53            value: &self.value[index],
54            path: child_path,
55        }
56    }
57
58    /// Index into the inner value, enables custom override to the tracked jsonpath location
59    pub fn index_custom(
60        &self,
61        index: impl Index,
62        index_custom: impl JSONPathIndex,
63    ) -> ValuePath<'a> {
64        let mut child_path = self.path.to_vec();
65        child_path.push(index_custom.jsonpath());
66        ValuePath {
67            value: &self.value[index],
68            path: child_path,
69        }
70    }
71
72    /// Lists all of the `ValuePath`s children by walking the inner value.
73    /// Includes flags for how to walk arrays
74    fn value_paths(self, explode_array: bool, inspect_arrays: bool) -> Vec<ValuePath<'a>> {
75        let mut paths = Vec::new();
76
77        match self.value {
78            Value::Object(map) => {
79                for (k, _) in map {
80                    let vp = self.index(k);
81                    let inner_paths = vp.value_paths(explode_array, inspect_arrays);
82                    paths.extend(inner_paths)
83                }
84            }
85            Value::Array(array) => {
86                if inspect_arrays {
87                    for (i, _array_value) in array.iter().enumerate() {
88                        let vp = self.index_custom(i, "[*]");
89                        let inner_paths = vp.value_paths(explode_array, inspect_arrays);
90                        paths.extend(inner_paths)
91                    }
92                } else if explode_array {
93                    for (i, _array_value) in array.iter().enumerate() {
94                        let vp = self.index(i);
95                        let inner_paths = vp.value_paths(explode_array, inspect_arrays);
96                        paths.extend(inner_paths)
97                    }
98                } else {
99                    paths.push(self)
100                }
101            }
102            Value::Null | Value::Bool(_) | Value::Number(_) | Value::String(_) => paths.push(self),
103        }
104        paths
105    }
106}
107
108pub trait JSONPathIndex: Index {
109    fn jsonpath(&self) -> String;
110}
111
112impl JSONPathIndex for usize {
113    fn jsonpath(&self) -> String {
114        format!("[{}]", self)
115    }
116}
117
118impl JSONPathIndex for str {
119    fn jsonpath(&self) -> String {
120        self.to_string()
121    }
122}
123
124impl JSONPathIndex for String {
125    fn jsonpath(&self) -> String {
126        self.to_string()
127    }
128}
129
130impl<'a, T> JSONPathIndex for &'a T
131where
132    T: ?Sized + JSONPathIndex,
133{
134    fn jsonpath(&self) -> String {
135        (**self).jsonpath()
136    }
137}
138
139pub trait ValuePaths {
140    fn value_paths(&self, explode_array: bool, inspect_arrays: bool) -> Vec<ValuePath>;
141}
142
143impl ValuePaths for Value {
144    /// Lists all of the `ValuePath` children by walking `Value`.
145    ///
146    /// Useful for generating full lists of JSONpaths within the Value
147    /// with [`ValuePath::jsonpath`]
148    ///
149    /// See also [`Value::json_paths`] from [`JSONPaths`]
150    fn value_paths(&self, explode_array: bool, inspect_arrays: bool) -> Vec<ValuePath> {
151        let base_valuepath = ValuePath::new(self, None);
152        base_valuepath.value_paths(explode_array, inspect_arrays)
153    }
154}
155
156// See https://github.com/rust-lang/rfcs/issues/997
157// impl<'a, I> std::ops::Index<I> for ValuePath<'a>
158// where I: JSONPathIndex {
159//     type Output = ValuePath<'a>;
160//     fn index(&self, index: I) -> &Self::Output {
161//         let mut child_path = self.path.clone();
162//         child_path.push(index.jsonpath());
163//         &ValuePath { value: &self.value[index], path: child_path }
164//     }
165// }
166
167pub trait JSONPaths {
168    /// Lists all of the json_paths by walking a JSON `self`.
169    /// Includes flags for how to walk arrays
170    fn json_paths(&self, explode_array: bool, inspect_arrays: bool) -> Vec<String>;
171
172    /// Maps all of the json_paths to the type they contain by walking a JSON `self`.
173    /// Includes flags for how to walk arrays
174    fn json_paths_types(
175        &self,
176        explode_array: bool,
177        inspect_arrays: bool,
178    ) -> IndexMap<String, String>;
179}
180
181impl JSONPaths for Value {
182    fn json_paths(&self, explode_array: bool, inspect_arrays: bool) -> Vec<String> {
183        self.value_paths(explode_array, inspect_arrays)
184            .into_iter()
185            .map(|value_path| value_path.jsonpath())
186            .collect()
187    }
188
189    fn json_paths_types(
190        &self,
191        explode_array: bool,
192        inspect_arrays: bool,
193    ) -> IndexMap<String, String> {
194        self.value_paths(explode_array, inspect_arrays)
195            .into_iter()
196            .map(|value_path| (value_path.jsonpath(), value_path.value.value_type()))
197            .collect()
198    }
199}
200
201#[cfg(test)]
202mod tests {
203    use super::*;
204
205    use serde_json::json;
206
207    #[test]
208    fn basic_valuepath() {
209        let v = json!({"key1": "value1", "key2": {"subkey1": "value1"}});
210        let vp_0 = ValuePath::new(&v, None);
211        assert_eq!(vp_0.jsonpath(), "$".to_string());
212
213        let v_1 = &v["key2"];
214        let vp_1 = vp_0.index("key2");
215        assert_eq!(vp_1.value, v_1);
216        assert_eq!(vp_1.path, vec!["key2".to_string()]);
217        assert_eq!(vp_1.jsonpath(), "$.key2".to_string());
218    }
219
220    #[test]
221    fn basic_valuepath_array() {
222        let v = json!({"key1": "value1", "key2": ["a", "b"]});
223        let vp_0 = ValuePath::new(&v, None);
224
225        let v_2 = &v["key2"][0];
226        let vp_1 = vp_0.index("key2");
227        let vp_2 = vp_1.index(0);
228
229        assert_eq!(vp_2.value, v_2);
230        assert_eq!(vp_2.path, vec!["key2".to_string(), "[0]".to_string()]);
231        assert_eq!(vp_1.jsonpath(), "$.key2".to_string());
232        assert_eq!(vp_2.jsonpath(), "$.key2[0]".to_string())
233    }
234
235    #[test]
236    fn parse_valuepaths() {
237        let v = json!({"key1": "value1", "key2": ["a", "b"]});
238        let vps = v.value_paths(false, false);
239
240        let vp_0 = ValuePath::new(&v, None);
241        let vp_1 = ValuePath::new(&v["key1"], Some(vec!["key1".to_string()]));
242        let vp_2 = ValuePath::new(&v["key2"], Some(vec!["key2".to_string()]));
243        let vp_1_alt = vp_0.index("key1");
244        let vp_2_alt = vp_0.index("key2");
245
246        let expected = vec![vp_1, vp_2];
247        let expected_alt = vec![vp_1_alt, vp_2_alt];
248
249        assert_eq!(vps, expected);
250        assert_eq!(vps, expected_alt);
251        assert_eq!(v.value_paths(false, false), expected);
252        assert_eq!(v.value_paths(false, false), expected_alt);
253    }
254
255    #[test]
256    fn parse_valuepaths_explode_array() {
257        let v = json!({"key1": "value1", "key2": ["a", "b"]});
258        let vps = v.value_paths(true, false);
259
260        let vp_0 = ValuePath::new(&v, None);
261        let vp_1 = ValuePath::new(&v["key1"], Some(vec!["key1".to_string()]));
262        let vp_2_1 = ValuePath::new(
263            &v["key2"][0],
264            Some(vec!["key2".to_string(), "[0]".to_string()]),
265        );
266        let vp_2_2 = ValuePath::new(
267            &v["key2"][1],
268            Some(vec!["key2".to_string(), "[1]".to_string()]),
269        );
270        let vp_1_alt = vp_0.index("key1");
271        let vp_2_1_alt = vp_0.index("key2").index(0);
272        let vp_2_2_alt = vp_0.index("key2").index(1);
273
274        assert_eq!(vps, vec![vp_1, vp_2_1, vp_2_2]);
275        assert_eq!(vps, vec![vp_1_alt, vp_2_1_alt, vp_2_2_alt]);
276    }
277
278    #[test]
279    fn parse_valuepaths_inspect_array() {
280        let v = json!({"key1": "value1", "key2": ["a", "b"]});
281        let vps = v.value_paths(false, true);
282
283        let vp_1 = ValuePath::new(&v["key1"], Some(vec!["key1".to_string()]));
284        let vp_2_1 = ValuePath::new(
285            &v["key2"][0],
286            Some(vec!["key2".to_string(), "[*]".to_string()]),
287        );
288        let vp_2_2 = ValuePath::new(
289            &v["key2"][1],
290            Some(vec!["key2".to_string(), "[*]".to_string()]),
291        );
292
293        assert_eq!(vps, vec![vp_1, vp_2_1, vp_2_2]);
294    }
295
296    #[test]
297    fn typical_parse_json_paths() {
298        let v = json!({"key1": "value1", "key2": {"subkey1": "value1"}});
299        let out_expected = vec![String::from("$.key1"), String::from("$.key2.subkey1")];
300        assert_eq!(v.json_paths(false, false), out_expected);
301    }
302
303    #[test]
304    fn trivial_parse_json_paths() {
305        let v = json!(1);
306        let out_expected = vec![String::from("$")];
307        assert_eq!(v.json_paths(false, false), out_expected);
308    }
309
310    #[test]
311    fn typical_parse_json_paths_types() {
312        let v = json!({"key1": "value1", "key2": {"subkey1": ["value1"]}});
313        let mut out_expected = IndexMap::new();
314        out_expected.insert("$.key1".to_string(), "String".to_string());
315        out_expected.insert("$.key2.subkey1".to_string(), "Array".to_string());
316        assert_eq!(v.json_paths_types(false, false), out_expected);
317    }
318
319    #[test]
320    fn trivial_parse_json_paths_types() {
321        let v = json!(1);
322        let mut out_expected = IndexMap::new();
323        out_expected.insert("$".to_string(), "Number".to_string());
324        assert_eq!(v.json_paths_types(false, false), out_expected);
325    }
326}