json_keypath_iter/
iter.rs

1use crate::style::{PresetStyle, Style};
2use serde_json::Value;
3use std::collections::VecDeque;
4
5/// Single element struct containing the path, set of array indices, and json value
6#[derive(Debug, PartialEq)]
7pub struct Element<'a> {
8    /// The full path from the base of a json structure to the value contained in the `Element`
9    pub path: String,
10    /// The full set of _array_ indices in the path, useful for grouping sets of `Element` structs to the same array element
11    pub indices: Vec<usize>,
12    /// The `serde_json::Value` of described by the path
13    pub value: &'a Value,
14}
15
16/// Iteration strict containing a queue of elements that still need to be yielded along with a style object
17#[derive(Debug)]
18pub struct Iter<'a> {
19    style: Style<'a>,
20    items: VecDeque<Element<'a>>,
21}
22
23/// Named `Iter` internally, but `Iterator` externally
24impl<'a> Iter<'a> {
25    /// Create a new json keypath iterator
26    ///
27    /// Example:
28    /// ```rust
29    /// use serde_json::json;
30    /// use json_keypath_iter::{Iterator, Element};
31    ///
32    /// let value = json!({"a": [1, 2]});
33    /// let iter = Iterator::new(&value);
34    /// let items: Vec<_> = iter.collect();
35    ///
36    /// assert_eq!(items[0], Element { path: "[\"a\"][0]".into(), indices: vec![0], value: &json!(1), });
37    /// assert_eq!(items[1], Element { path: "[\"a\"][1]".into(), indices: vec![1], value: &json!(2), });
38    /// ```
39    pub fn new(json: &'a Value) -> Self {
40        let mut queue = VecDeque::new();
41        queue.push_back(Element {
42            path: String::from(""),
43            indices: Vec::new(),
44            value: json,
45        });
46
47        Self {
48            items: queue,
49            style: PresetStyle::SquareBrackets.into(),
50        }
51    }
52
53    /// Optionally used to set a custom style for the path in elements
54    ///
55    /// Example:
56    /// ```rust
57    /// use serde_json::json;
58    /// use json_keypath_iter::{Style, PresetStyle, Iterator, Element};
59    ///
60    /// let style: Style = PresetStyle::CommonJs.into();
61    /// let value = json!({"x42": [true, [null, "Hello there."]]});
62    /// let iter = Iterator::new(&value).use_style(style);
63    /// let items: Vec<_> = iter.collect();
64    ///
65    /// assert_eq!(items[0], Element { path: ".x42[0]".into(), indices: vec![0], value: &json!(true), });
66    /// assert_eq!(items[1], Element { path: ".x42[1][0]".into(), indices: vec![1, 0], value: &json!(null), });
67    /// assert_eq!(items[2], Element { path: ".x42[1][1]".into(), indices: vec![1, 1], value: &json!("Hello there."), });
68    /// ```
69    pub fn use_style(mut self, style: Style<'a>) -> Self {
70        self.style = style;
71        self
72    }
73}
74
75impl<'a> From<&'a Value> for Iter<'a> {
76    fn from(item: &'a Value) -> Iter<'a> {
77        Iter::new(item)
78    }
79}
80
81impl<'a> Iterator for Iter<'a> {
82    type Item = Element<'a>;
83
84    fn next(&mut self) -> Option<Self::Item> {
85        'items: while let Some(el) = self.items.pop_front() {
86            match el.value {
87                Value::Object(obj) => {
88                    for (key, val) in obj.iter().rev() {
89                        self.items.push_front(Element {
90                            path: self.style.object_format(&el.path, key),
91                            indices: el.indices.clone(),
92                            value: val,
93                        });
94                    }
95
96                    match self.style.should_skip_object_parents() {
97                        true => continue 'items,
98                        false => return Some(el),
99                    };
100                }
101                Value::Array(arr) => {
102                    for (index, val) in arr.iter().enumerate().rev() {
103                        let mut indices_vec = el.indices.to_vec();
104                        indices_vec.push(index);
105
106                        self.items.push_front(Element {
107                            path: self.style.array_format(&el.path, index),
108                            indices: indices_vec,
109                            value: val,
110                        });
111                    }
112
113                    match self.style.should_skip_array_parents() {
114                        true => continue 'items,
115                        false => return Some(el),
116                    };
117                }
118                _ => return Some(el),
119            }
120        }
121        None
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128    use crate::style::StyleBuilder;
129    use serde_json::json;
130
131    #[test]
132    fn null_to_iter() {
133        let value = json!(null);
134        let items: Vec<_> = Iter::new(&value).collect();
135
136        assert_eq!(items.len(), 1);
137        assert_eq!(
138            items[0],
139            Element {
140                path: String::from(""),
141                indices: Vec::new(),
142                value: &Value::Null,
143            }
144        );
145    }
146
147    #[test]
148    fn bool_to_iter() {
149        let value = json!(true);
150        let items: Vec<_> = Iter::new(&value).collect();
151
152        assert_eq!(items.len(), 1);
153        assert_eq!(
154            items[0],
155            Element {
156                path: String::from(""),
157                indices: Vec::new(),
158                value: &Value::Bool(true),
159            }
160        );
161    }
162
163    #[test]
164    fn number_to_iter() {
165        let value = json!(42);
166        let items: Vec<_> = Iter::new(&value).collect();
167
168        assert_eq!(items.len(), 1);
169        assert_eq!(
170            items[0],
171            Element {
172                path: String::from(""),
173                indices: Vec::new(),
174                value: &Value::Number(42.into()),
175            }
176        );
177    }
178
179    #[test]
180    fn string_to_iter() {
181        let value = json!("Hello there!");
182        let items: Vec<_> = Iter::new(&value).collect();
183
184        assert_eq!(items.len(), 1);
185        assert_eq!(
186            items[0],
187            Element {
188                path: String::from(""),
189                indices: Vec::new(),
190                value: &Value::String("Hello there!".into()),
191            }
192        );
193    }
194
195    #[test]
196    fn array_to_iter() {
197        let value = json!([null, null]);
198        let style = StyleBuilder::new().include_array_parents().build();
199        let items: Vec<_> = Iter::new(&value).use_style(style).collect();
200
201        assert_eq!(items.len(), 3);
202        assert_eq!(
203            items[0],
204            Element {
205                path: String::from(""),
206                indices: Vec::new(),
207                value: &Value::Array(vec![Value::Null, Value::Null]),
208            }
209        );
210    }
211
212    #[test]
213    fn object_to_iter() {
214        let value = json!({ "a": true, "b": false });
215        let style = StyleBuilder::new().include_object_parents().build();
216        let items: Vec<_> = Iter::new(&value).use_style(style).collect();
217
218        assert_eq!(items.len(), 3);
219        assert_eq!(
220            items[0],
221            Element {
222                path: String::from(""),
223                indices: Vec::new(),
224                value: &json!({ "a": true, "b": false }),
225            }
226        );
227    }
228
229    #[test]
230    fn can_skip_parents() {
231        let value = json!({
232            "first": [1, 2, 3],
233            "middle": true,
234            "last": ["a", "b", "c"],
235        });
236        let style = StyleBuilder::new()
237            .skip_object_parents()
238            .skip_array_parents()
239            .build();
240        let items: Vec<_> = Iter::new(&value).use_style(style).collect();
241
242        assert_eq!(items.len(), 7);
243        assert_eq!(
244            items[2],
245            Element {
246                path: String::from("[\"first\"][2]"),
247                indices: vec![2],
248                value: &Value::Number(3.into()),
249            }
250        );
251        assert_eq!(
252            items[5],
253            Element {
254                path: String::from("[\"last\"][2]"),
255                indices: vec![2],
256                value: &Value::String("c".into()),
257            }
258        );
259    }
260
261    #[test]
262    fn custom_style_on_iter() {
263        let value = json!({
264            "first": [1, 2, 3],
265        });
266        let style = StyleBuilder::new()
267            .object_key_prefix("!")
268            .object_key_suffix("@")
269            .show_object_keys_in_path()
270            .include_object_parents()
271            .array_key_prefix("#")
272            .array_key_suffix("$")
273            .hide_array_keys_in_path()
274            .include_array_parents()
275            .build();
276        let items: Vec<_> = Iter::new(&value).use_style(style).collect();
277
278        assert_eq!(items.len(), 5);
279        assert_eq!(
280            items[3],
281            Element {
282                path: String::from("!first@#$"),
283                indices: vec![1],
284                value: &Value::Number(2.into()),
285            }
286        );
287    }
288
289    #[test]
290    fn complex_format_on_iter() {
291        let value = json!({
292            "first": [1, 2, 3],
293            "middle": true,
294            "last": ["a", "b", "c"],
295        });
296        let style = StyleBuilder::new()
297            .include_object_parents()
298            .include_array_parents()
299            .build();
300        let items: Vec<_> = Iter::new(&value).use_style(style).collect();
301
302        assert_eq!(items.len(), 10);
303        assert_eq!(
304            items[2],
305            Element {
306                path: String::from("[\"first\"][0]"),
307                indices: vec![0],
308                value: &Value::Number(1.into()),
309            }
310        );
311        assert_eq!(
312            items[5],
313            Element {
314                path: String::from("[\"last\"]"),
315                indices: Vec::new(),
316                value: &Value::Array(vec!["a".into(), "b".into(), "c".into()]),
317            }
318        );
319        assert_eq!(
320            items[8],
321            Element {
322                path: String::from("[\"last\"][2]"),
323                indices: vec![2],
324                value: &Value::String("c".into()),
325            }
326        );
327
328        // interesting note that "middle" is sorted alphabetically to the last object entry by json!()
329        assert_eq!(
330            items[9],
331            Element {
332                path: String::from("[\"middle\"]"),
333                indices: Vec::new(),
334                value: &Value::Bool(true),
335            }
336        );
337    }
338
339    #[test]
340    fn in_a_for_loop() {
341        let value = json!({
342            "first": [1, 2, 3],
343            "middle": true,
344            "last": ["a", "b", "c"],
345        });
346
347        let mut collection = Vec::new();
348        let style = StyleBuilder::new()
349            .include_object_parents()
350            .include_array_parents()
351            .build();
352        for item in Iter::new(&value).use_style(style) {
353            collection.push(item);
354        }
355
356        assert_eq!(collection.len(), 10);
357        assert_eq!(
358            collection[2],
359            Element {
360                path: String::from("[\"first\"][0]"),
361                indices: vec![0],
362                value: &Value::Number(1.into()),
363            }
364        );
365        assert_eq!(
366            collection[5],
367            Element {
368                path: String::from("[\"last\"]"),
369                indices: Vec::new(),
370                value: &Value::Array(vec!["a".into(), "b".into(), "c".into()]),
371            }
372        );
373        assert_eq!(
374            collection[8],
375            Element {
376                path: String::from("[\"last\"][2]"),
377                indices: vec![2],
378                value: &Value::String("c".into()),
379            }
380        );
381
382        // interesting note that "middle" is sorted alphabetically to the last object entry by json!()
383        assert_eq!(
384            collection[9],
385            Element {
386                path: String::from("[\"middle\"]"),
387                indices: Vec::new(),
388                value: &Value::Bool(true),
389            }
390        );
391    }
392}