use crate::{
CrawlerError, CrawlerResult, JsonCrawler, JsonCrawlerBorrowed, JsonCrawlerOwned, JsonPath,
PathList,
};
use std::{borrow::Borrow, slice::IterMut, sync::Arc, vec::IntoIter};
pub trait JsonCrawlerIterator: Iterator
where
Self::Item: JsonCrawler,
{
fn find_path(self, path: impl AsRef<str>) -> CrawlerResult<Self::Item>;
fn get_context(&self) -> JsonCrawlerArrayIterContext;
fn try_last(self) -> CrawlerResult<Self::Item>;
}
pub struct JsonCrawlerArrayIterContext {
pub(crate) source: Arc<String>,
pub(crate) path: String,
}
pub struct JsonCrawlerArrayIterMut<'a> {
pub(crate) source: Arc<String>,
pub(crate) array: IterMut<'a, serde_json::Value>,
pub(crate) path: PathList,
pub(crate) cur_front: usize,
pub(crate) cur_back: usize,
}
#[derive(Clone)]
pub struct JsonCrawlerArrayIntoIter {
pub(crate) source: Arc<String>,
pub(crate) array: IntoIter<serde_json::Value>,
pub(crate) path: PathList,
pub(crate) cur_front: usize,
pub(crate) cur_back: usize,
}
impl<'a> Iterator for JsonCrawlerArrayIterMut<'a> {
type Item = JsonCrawlerBorrowed<'a>;
fn next(&mut self) -> Option<Self::Item> {
let crawler = self.array.next()?;
let out = Some(JsonCrawlerBorrowed {
source: self.source.clone(),
crawler,
path: self.path.clone().with(JsonPath::IndexNum(self.cur_front)),
});
self.cur_front += 1;
out
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.array.len(), Some(self.array.len()))
}
}
impl<'a> ExactSizeIterator for JsonCrawlerArrayIterMut<'a> {}
impl<'a> DoubleEndedIterator for JsonCrawlerArrayIterMut<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
let crawler = self.array.next_back()?;
let out = Some(JsonCrawlerBorrowed {
source: self.source.clone(),
crawler,
path: self.path.clone().with(JsonPath::IndexNum(self.cur_back)),
});
self.cur_back = self.cur_back.saturating_sub(1);
out
}
}
impl<'a> JsonCrawlerIterator for JsonCrawlerArrayIterMut<'a> {
fn find_path(mut self, path: impl AsRef<str>) -> CrawlerResult<Self::Item> {
self.find_map(|crawler| crawler.navigate_pointer(path.as_ref()).ok())
.ok_or_else(|| {
CrawlerError::path_not_found_in_array(self.path, self.source, path.as_ref())
})
}
fn get_context(&self) -> JsonCrawlerArrayIterContext {
JsonCrawlerArrayIterContext {
source: self.source.clone(),
path: self.path.borrow().into(),
}
}
fn try_last(self) -> CrawlerResult<Self::Item> {
let Self {
source,
array,
mut path,
..
} = self;
let len = array.len();
path.push(JsonPath::IndexNum(len));
let Some(last_item) = array.last() else {
return Err(CrawlerError::array_size(path, source, 0));
};
Ok(Self::Item {
source,
crawler: last_item,
path,
})
}
}
impl Iterator for JsonCrawlerArrayIntoIter {
type Item = JsonCrawlerOwned;
fn next(&mut self) -> Option<Self::Item> {
let crawler = self.array.next()?;
let out = Some(JsonCrawlerOwned {
source: self.source.clone(),
crawler,
path: self.path.clone().with(JsonPath::IndexNum(self.cur_front)),
});
self.cur_front += 1;
out
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.array.len(), Some(self.array.len()))
}
}
impl ExactSizeIterator for JsonCrawlerArrayIntoIter {}
impl DoubleEndedIterator for JsonCrawlerArrayIntoIter {
fn next_back(&mut self) -> Option<Self::Item> {
let crawler = self.array.next_back()?;
let out = Some(JsonCrawlerOwned {
source: self.source.clone(),
crawler,
path: self.path.clone().with(JsonPath::IndexNum(self.cur_back)),
});
self.cur_back = self.cur_back.saturating_sub(1);
out
}
}
impl JsonCrawlerIterator for JsonCrawlerArrayIntoIter {
fn find_path(mut self, path: impl AsRef<str>) -> CrawlerResult<Self::Item> {
self.find_map(|crawler| crawler.navigate_pointer(path.as_ref()).ok())
.ok_or_else(|| {
CrawlerError::path_not_found_in_array(self.path, self.source, path.as_ref())
})
}
fn get_context(&self) -> JsonCrawlerArrayIterContext {
JsonCrawlerArrayIterContext {
source: self.source.clone(),
path: self.path.borrow().into(),
}
}
fn try_last(self) -> CrawlerResult<Self::Item> {
let Self {
source,
array,
mut path,
..
} = self;
let len = array.len();
path.push(JsonPath::IndexNum(len));
let Some(last_item) = array.last() else {
return Err(CrawlerError::array_size(path, source, 0));
};
Ok(Self::Item {
source,
crawler: last_item,
path,
})
}
}