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