use std::marker::PhantomData;
use std::sync::Arc;
use reqwest::Method;
use serde::de::DeserializeOwned;
use crate::client::states::Registered;
use crate::params::Parameters;
use crate::responses::ComResult;
use crate::{WebwareClient, WWClientResult};
pub trait HasList<T> {
fn into_items(self) -> Option<Vec<T>>;
}
pub trait HasComResult {
fn comresult(&self) -> &ComResult;
}
pub struct CursoredResponse<T, R>
where
R: HasList<T>,
{
client: Arc<WebwareClient<Registered>>,
method: Method,
function: String,
version: u32,
base_params: Parameters,
page_size: u32,
_phantom: PhantomData<(T, R)>,
finished: bool,
}
pub struct ItemsWithComResult<T: Clone> {
pub items: Option<Vec<T>>,
pub comresult: ComResult,
}
impl<T: Clone> ItemsWithComResult<T> {
pub fn items(items: &[T], comresult: ComResult) -> Self {
Self {
items: Some(items.to_vec()),
comresult,
}
}
pub fn no_items(comresult: ComResult) -> Self {
Self {
items: None,
comresult,
}
}
}
impl<T, R> CursoredResponse<T, R>
where
T: DeserializeOwned + Clone,
R: DeserializeOwned + HasList<T> + HasComResult,
{
pub async fn next_with_comresult(&mut self) -> WWClientResult<Option<ItemsWithComResult<T>>> {
if self.finished {
return Ok(None);
}
if !self.client.has_cursor().await {
self.client.create_cursor(self.page_size).await;
}
let response = self
.client
.request_generic::<R>(
self.method.clone(),
&self.function,
self.version,
self.base_params.clone(),
None,
)
.await?;
let comresult = response.comresult().clone();
if self.client.cursor_closed().await {
tracing::debug!(comresult=?comresult, "Cursor closed as indicated by the server");
self.finished = true;
self.client.close_cursor().await;
}
let items = response.into_items();
match items {
Some(ref list) if list.is_empty() => {
tracing::warn!(comresult=?comresult, "Empty list received from server, closing cursor");
self.finished = true;
self.client.close_cursor().await;
Ok(Some(ItemsWithComResult::no_items(comresult)))
}
Some(list) => Ok(Some(ItemsWithComResult::items(&list, comresult))),
None => {
tracing::warn!(comresult=?comresult, "No list received from server, closing cursor");
self.finished = true;
self.client.close_cursor().await;
Ok(Some(ItemsWithComResult::no_items(comresult)))
}
}
}
pub async fn collect_all_with_comresult(&mut self) -> WWClientResult<Vec<T>> {
let mut all_items = Vec::new();
while let Some(batch) = self.next_with_comresult().await? {
if let Some(items) = batch.items {
all_items.extend(items);
}
}
Ok(all_items)
}
}
impl<T, R> CursoredResponse<T, R>
where
T: DeserializeOwned + Clone,
R: DeserializeOwned + HasList<T>,
{
pub(crate) fn new(
client: Arc<WebwareClient<Registered>>,
method: Method,
function: String,
version: u32,
base_params: Parameters,
page_size: u32,
) -> Self {
Self {
client,
method,
function,
version,
base_params,
page_size,
_phantom: PhantomData,
finished: false,
}
}
pub async fn next(&mut self) -> WWClientResult<Option<Vec<T>>> {
if self.finished {
return Ok(None);
}
if !self.client.has_cursor().await {
self.client.create_cursor(self.page_size).await;
}
let response = self
.client
.request_generic::<R>(
self.method.clone(),
&self.function,
self.version,
self.base_params.clone(),
None,
)
.await?;
if self.client.cursor_closed().await {
tracing::debug!("Cursor closed as indicated by the server");
self.finished = true;
self.client.close_cursor().await;
}
let items = response.into_items();
match items {
Some(ref list) if list.is_empty() => {
tracing::warn!("Empty list received from server, closing cursor");
self.finished = true;
self.client.close_cursor().await;
Ok(None)
}
Some(list) => Ok(Some(list)),
None => {
tracing::warn!("No list received from server, closing cursor");
self.finished = true;
self.client.close_cursor().await;
Ok(None)
}
}
}
pub async fn collect_all(&mut self) -> WWClientResult<Vec<T>> {
let mut all_items = Vec::new();
while let Some(batch) = self.next().await? {
all_items.extend(batch);
}
Ok(all_items)
}
pub fn is_finished(&self) -> bool {
self.finished
}
}
pub trait CursoredRequests {
fn cursored_request<T, R>(
&self,
method: Method,
function: &str,
version: u32,
params: Parameters,
page_size: u32,
) -> impl std::future::Future<Output = WWClientResult<CursoredResponse<T, R>>> + Send
where
T: DeserializeOwned + Clone,
R: DeserializeOwned + HasList<T> + HasComResult;
}
impl CursoredRequests for WebwareClient<Registered> {
fn cursored_request<T, R>(
&self,
_method: Method,
_function: &str,
_version: u32,
_params: Parameters,
_page_size: u32,
) -> impl std::future::Future<Output = WWClientResult<CursoredResponse<T, R>>> + Send
where
T: DeserializeOwned + Clone,
R: DeserializeOwned + HasList<T> + HasComResult,
{
async move {
Err(crate::error::WWSVCError::NotAuthenticated)
}
}
}
impl CursoredRequests for Arc<WebwareClient<Registered>> {
fn cursored_request<T, R>(
&self,
method: Method,
function: &str,
version: u32,
params: Parameters,
page_size: u32,
) -> impl std::future::Future<Output = WWClientResult<CursoredResponse<T, R>>> + Send
where
T: DeserializeOwned + Clone,
R: DeserializeOwned + HasList<T> + HasComResult,
{
async move {
Ok(CursoredResponse::new(
self.clone(),
method,
function.to_string(),
version,
params,
page_size,
))
}
}
}