#[cfg(any(feature = "async", feature = "sync"))]
use std::borrow::Cow;
use std::{fmt::Debug, marker::PhantomData};
#[cfg(any(feature = "async", feature = "sync"))]
use log::trace;
#[cfg(any(feature = "async", feature = "sync"))]
use reqwest::Method;
use serde::{de::DeserializeOwned, Serialize};
pub(crate) use self::private::PageObject;
#[cfg(feature = "async")]
use crate::client::request_builder::AsyncRequestBuilder;
#[cfg(feature = "sync")]
use crate::client::request_builder::SyncRequestBuilder;
#[cfg(any(feature = "async", feature = "sync"))]
use crate::client::request_builder::{BaseRequestBuilderContainer, RequestBuilder, TryFromEmptyResponse};
mod private {
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PageObject<T>
where
T: Serialize,
{
pub items: Vec<T>,
pub next: Option<String>,
#[allow(dead_code)]
limit: usize,
#[allow(dead_code)]
offset: usize,
#[allow(dead_code)]
total: usize,
}
}
#[cfg(any(feature = "async", feature = "sync"))]
struct PageRequestBuilder<TClient, TInner>(RequestBuilder<TClient, TInner>);
#[doc(hidden)]
pub trait PageInformation<T>
where
Self: crate::private::Sealed,
{
type Items: IntoIterator<Item = T>;
fn items(&self) -> Self::Items;
fn take_items(self) -> Self::Items;
fn next(self) -> Option<String>;
}
#[derive(Debug)]
pub struct Page<TInner, TItem>
where
TInner: PageInformation<TItem> + DeserializeOwned + Debug,
{
pub(crate) inner: TInner,
pub(crate) phantom: PhantomData<TItem>,
}
#[cfg(any(feature = "async", feature = "sync"))]
impl<TClient, TInner> BaseRequestBuilderContainer<TClient, TInner> for PageRequestBuilder<TClient, TInner> {
fn new<S>(method: Method, base_url: S, client: TClient) -> Self
where
S: Into<Cow<'static, str>>,
{
Self(RequestBuilder::new(method, base_url, client))
}
fn new_with_body<S>(method: Method, base_url: S, body: (), client: TClient) -> Self
where
S: Into<Cow<'static, str>>,
{
Self(RequestBuilder::new_with_body(method, base_url, body, client))
}
fn take_base_builder(self) -> RequestBuilder<TClient, TInner> {
self.0
}
fn get_base_builder_mut(&mut self) -> &mut RequestBuilder<TClient, TInner> {
&mut self.0
}
}
impl<T> crate::private::Sealed for PageObject<T> where T: Serialize {}
impl<TItem, TReturn> PageInformation<TReturn> for PageObject<TItem>
where
TItem: ToOwned + TryInto<TReturn> + Serialize,
TReturn: TryFrom<<TItem as ToOwned>::Owned>,
{
type Items = Vec<TReturn>;
fn items(&self) -> Self::Items {
self.items
.iter()
.filter_map(|item| item.to_owned().try_into().ok())
.collect()
}
fn take_items(self) -> Self::Items {
self.items.into_iter().filter_map(|item| item.try_into().ok()).collect()
}
fn next(self) -> Option<String> {
self.next
}
}
impl<TInner, TItem> Page<TInner, TItem>
where
TInner: PageInformation<TItem> + DeserializeOwned + Debug,
{
pub fn items(&self) -> TInner::Items {
self.inner.items()
}
pub fn take_items(self) -> TInner::Items {
self.inner.take_items()
}
}
#[cfg(feature = "async")]
impl<TInner, TItem> Page<TInner, TItem>
where
TInner: PageInformation<TItem> + DeserializeOwned + Debug + TryFromEmptyResponse + Send + Sync,
{
pub async fn next_page_async<C>(self, client: &'_ C) -> crate::error::Result<Option<Page<TInner, TItem>>>
where
C: crate::client::private::BuildHttpRequestAsync
+ crate::client::private::AccessTokenExpiryAsync
+ Clone
+ Send
+ Sync,
{
if let Some(url) = self.inner.next() {
let next_page = PageRequestBuilder::new(Method::GET, url, client.clone())
.send_async()
.await?;
trace!("Next page: {next_page:?}");
Ok(Some(Page {
inner: next_page,
phantom: PhantomData,
}))
} else {
Ok(None)
}
}
}
#[cfg(feature = "sync")]
impl<TInner, TItem> Page<TInner, TItem>
where
TInner: PageInformation<TItem> + DeserializeOwned + Debug + TryFromEmptyResponse,
{
pub fn next_page_sync<C>(self, client: &'_ C) -> crate::error::Result<Option<Page<TInner, TItem>>>
where
C: crate::client::private::BuildHttpRequestSync + crate::client::private::AccessTokenExpirySync + Clone,
{
if let Some(url) = self.inner.next() {
let next_page = PageRequestBuilder::new(Method::GET, url, client.clone()).send_sync()?;
trace!("Next page: {next_page:?}");
Ok(Some(Page {
inner: next_page,
phantom: PhantomData,
}))
} else {
Ok(None)
}
}
}