use crate::{model::Page, ClientError, ClientResult};
pub type Paginator<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
pub fn paginate_with_ctx<'a, Ctx: 'a, T: 'a, Request: 'a>(
ctx: Ctx,
req: Request,
page_size: u32,
) -> Paginator<'a, ClientResult<T>>
where
Request: Fn(&Ctx, u32, u32) -> ClientResult<Page<T>>,
{
paginate(move |limit, offset| req(&ctx, limit, offset), page_size)
}
pub fn paginate<'a, T: 'a, Request: 'a>(
req: Request,
page_size: u32,
) -> Paginator<'a, ClientResult<T>>
where
Request: Fn(u32, u32) -> ClientResult<Page<T>>,
{
let pages = PageIterator {
req,
offset: 0,
done: false,
page_size,
};
Box::new(pages.flat_map(|result| ResultIter::new(result.map(|page| page.items.into_iter()))))
}
struct PageIterator<Request> {
req: Request,
offset: u32,
done: bool,
page_size: u32,
}
impl<T, Request> Iterator for PageIterator<Request>
where
Request: Fn(u32, u32) -> ClientResult<Page<T>>,
{
type Item = ClientResult<Page<T>>;
fn next(&mut self) -> Option<Self::Item> {
if self.done {
return None;
}
match (self.req)(self.page_size, self.offset) {
Ok(page) if page.items.is_empty() => {
self.done = true;
None
}
Ok(page) => {
self.offset += page.items.len() as u32;
Some(Ok(page))
}
Err(e) => Some(Err(e)),
}
}
}
struct ResultIter<T, I: Iterator<Item = T>> {
inner: Option<I>,
err: Option<ClientError>,
}
impl<T, I: Iterator<Item = T>> ResultIter<T, I> {
pub fn new(res: ClientResult<I>) -> Self {
match res {
Ok(inner) => ResultIter {
inner: Some(inner),
err: None,
},
Err(err) => ResultIter {
inner: None,
err: Some(err),
},
}
}
}
impl<T, I: Iterator<Item = T>> Iterator for ResultIter<T, I> {
type Item = ClientResult<T>;
fn next(&mut self) -> Option<Self::Item> {
match (self.err.take(), &mut self.inner) {
(Some(err), _) => Some(Err(err)),
(None, Some(inner)) => inner.next().map(Ok),
_ => None, }
}
}