json_crawler/
iter.rs

1//! Iterators and extension for working with crawlers that are pointing to
2//! arrays.
3use crate::{
4    CrawlerError, CrawlerResult, JsonCrawler, JsonCrawlerBorrowed, JsonCrawlerOwned, JsonPath,
5    PathList,
6};
7use std::slice::IterMut;
8use std::sync::Arc;
9use std::vec::IntoIter;
10
11/// Iterator extension trait containing special methods for Json Crawler
12/// iterators to help with error handling.
13pub trait JsonCrawlerIterator: Iterator
14where
15    Self::Item: JsonCrawler,
16{
17    /// Return the first crawler found at `path`, or error.
18    fn find_path(self, path: impl AsRef<str>) -> CrawlerResult<Self::Item>;
19    /// Return the last item of the array, or return an error with context.
20    fn try_last(self) -> CrawlerResult<Self::Item>;
21}
22
23pub struct JsonCrawlerArrayIterMut<'a> {
24    pub(crate) source: Arc<String>,
25    pub(crate) array: IterMut<'a, serde_json::Value>,
26    pub(crate) path: PathList,
27    pub(crate) cur_front: usize,
28    pub(crate) cur_back: usize,
29}
30
31#[derive(Clone)]
32pub struct JsonCrawlerArrayIntoIter {
33    pub(crate) source: Arc<String>,
34    pub(crate) array: IntoIter<serde_json::Value>,
35    pub(crate) path: PathList,
36    pub(crate) cur_front: usize,
37    pub(crate) cur_back: usize,
38}
39
40impl<'a> Iterator for JsonCrawlerArrayIterMut<'a> {
41    type Item = JsonCrawlerBorrowed<'a>;
42    fn next(&mut self) -> Option<Self::Item> {
43        let crawler = self.array.next()?;
44        let out = Some(JsonCrawlerBorrowed {
45            // Low cost as this is an Arc
46            source: self.source.clone(),
47            crawler,
48            // Ideally there should be a Borrowed version of this struct - otherwise we need to
49            // clone every time here.
50            path: self.path.clone().with(JsonPath::IndexNum(self.cur_front)),
51        });
52        self.cur_front += 1;
53        out
54    }
55    // Required to be exact to implement ExactSizeIterator.
56    fn size_hint(&self) -> (usize, Option<usize>) {
57        (self.array.len(), Some(self.array.len()))
58    }
59}
60
61// Default implementation is correct, due to implementation of size_hint.
62impl ExactSizeIterator for JsonCrawlerArrayIterMut<'_> {}
63
64impl DoubleEndedIterator for JsonCrawlerArrayIterMut<'_> {
65    fn next_back(&mut self) -> Option<Self::Item> {
66        let crawler = self.array.next_back()?;
67        let out = Some(JsonCrawlerBorrowed {
68            // Low cost as this is an Arc
69            source: self.source.clone(),
70            crawler,
71            // Ideally there should be a Borrowed version of this struct - otherwise we need to
72            // clone every time here.
73            path: self.path.clone().with(JsonPath::IndexNum(self.cur_back)),
74        });
75        self.cur_back = self.cur_back.saturating_sub(1);
76        out
77    }
78}
79
80impl JsonCrawlerIterator for JsonCrawlerArrayIterMut<'_> {
81    fn find_path(mut self, path: impl AsRef<str>) -> CrawlerResult<Self::Item> {
82        self.find_map(|crawler| crawler.navigate_pointer(path.as_ref()).ok())
83            .ok_or_else(|| {
84                CrawlerError::path_not_found_in_array(self.path, self.source, path.as_ref())
85            })
86    }
87    fn try_last(self) -> CrawlerResult<Self::Item> {
88        let Self {
89            source,
90            mut array,
91            mut path,
92            ..
93        } = self;
94        let len = array.len();
95        path.push(JsonPath::IndexNum(len - 1));
96        let Some(last_item) = array.next_back() else {
97            return Err(CrawlerError::array_size(path, source, 0));
98        };
99        Ok(Self::Item {
100            source,
101            crawler: last_item,
102            path,
103        })
104    }
105}
106
107impl Iterator for JsonCrawlerArrayIntoIter {
108    type Item = JsonCrawlerOwned;
109    fn next(&mut self) -> Option<Self::Item> {
110        let crawler = self.array.next()?;
111        let out = Some(JsonCrawlerOwned {
112            // Low cost as this is an Arc
113            source: self.source.clone(),
114            crawler,
115            // Ideally there should be a Borrowed version of this struct - otherwise we need to
116            // clone every time here.
117            path: self.path.clone().with(JsonPath::IndexNum(self.cur_front)),
118        });
119        self.cur_front += 1;
120        out
121    }
122    // Required to be exact to implement ExactSizeIterator.
123    fn size_hint(&self) -> (usize, Option<usize>) {
124        (self.array.len(), Some(self.array.len()))
125    }
126}
127// Default implementation is correct, due to implementation of size_hint.
128impl ExactSizeIterator for JsonCrawlerArrayIntoIter {}
129
130impl DoubleEndedIterator for JsonCrawlerArrayIntoIter {
131    fn next_back(&mut self) -> Option<Self::Item> {
132        let crawler = self.array.next_back()?;
133        let out = Some(JsonCrawlerOwned {
134            // Low cost as this is an Arc
135            source: self.source.clone(),
136            crawler,
137            // Ideally there should be a Borrowed version of this struct - otherwise we need to
138            // clone every time here.
139            path: self.path.clone().with(JsonPath::IndexNum(self.cur_back)),
140        });
141        self.cur_back = self.cur_back.saturating_sub(1);
142        out
143    }
144}
145impl JsonCrawlerIterator for JsonCrawlerArrayIntoIter {
146    fn find_path(mut self, path: impl AsRef<str>) -> CrawlerResult<Self::Item> {
147        self.find_map(|crawler| crawler.navigate_pointer(path.as_ref()).ok())
148            .ok_or_else(|| {
149                CrawlerError::path_not_found_in_array(self.path, self.source, path.as_ref())
150            })
151    }
152    fn try_last(self) -> CrawlerResult<Self::Item> {
153        let Self {
154            source,
155            mut array,
156            mut path,
157            ..
158        } = self;
159        let len = array.len();
160        path.push(JsonPath::IndexNum(len - 1));
161        let Some(last_item) = array.next_back() else {
162            return Err(CrawlerError::array_size(path, source, 0));
163        };
164        Ok(Self::Item {
165            source,
166            crawler: last_item,
167            path,
168        })
169    }
170}