use crate::error::Error;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) type BoxFuture<T> = std::pin::Pin<Box<dyn Future<Output = T> + Send>>;
#[cfg(target_arch = "wasm32")]
pub(crate) type BoxFuture<T> = std::pin::Pin<Box<dyn Future<Output = T>>>;
#[cfg(not(target_arch = "wasm32"))]
pub(crate) type PageFetcher<T> = Box<dyn Fn() -> BoxFuture<Result<Page<T>, Error>> + Send + Sync>;
#[cfg(target_arch = "wasm32")]
pub(crate) type PageFetcher<T> = Box<dyn Fn() -> BoxFuture<Result<Page<T>, Error>>>;
pub struct Page<T> {
pub items: Vec<T>,
pub page: u64,
pub page_size: u64,
pub total: u64,
pub has_next: bool,
pub(crate) next: Option<PageFetcher<T>>,
}
impl<T: std::fmt::Debug> std::fmt::Debug for Page<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Page")
.field("items", &self.items)
.field("page", &self.page)
.field("page_size", &self.page_size)
.field("total", &self.total)
.field("has_next", &self.has_next)
.finish_non_exhaustive()
}
}
impl<T> Page<T> {
pub async fn next_page(&self) -> Result<Option<Page<T>>, Error> {
match (&self.next, self.has_next) {
(Some(fetch), true) => fetch().await.map(Some),
_ => Ok(None),
}
}
}
#[cfg(feature = "stream")]
mod stream {
use super::*;
use std::pin::Pin;
use std::task::{Context, Poll};
pub struct PageStream<T> {
buffer: std::collections::VecDeque<T>,
next: Option<PageFetcher<T>>,
has_next: bool,
pending: Option<BoxFuture<Result<Page<T>, Error>>>,
}
impl<T> Page<T> {
pub fn stream(self) -> PageStream<T> {
PageStream {
buffer: self.items.into(),
next: self.next,
has_next: self.has_next,
pending: None,
}
}
}
impl<T> futures_core::Stream for PageStream<T> {
type Item = Result<T, Error>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let this = self.get_mut();
loop {
if let Some(item) = this.buffer.pop_front() {
return Poll::Ready(Some(Ok(item)));
}
if let Some(pending) = this.pending.as_mut() {
match pending.as_mut().poll(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(Ok(page)) => {
this.pending = None;
this.buffer = page.items.into();
this.has_next = page.has_next;
this.next = page.next;
continue;
}
Poll::Ready(Err(err)) => {
this.pending = None;
this.has_next = false;
this.next = None;
return Poll::Ready(Some(Err(err)));
}
}
}
match (&this.next, this.has_next) {
(Some(fetch), true) => {
this.pending = Some(fetch());
}
_ => return Poll::Ready(None),
}
}
}
}
impl<T> Unpin for PageStream<T> {}
}
#[cfg(feature = "stream")]
pub use stream::PageStream;