use crate::client::apis;
use std::fmt::Debug;
pub trait PaginationParams {
fn offset(&self) -> i64;
fn set_offset(&mut self, offset: i64);
fn limit(&self) -> Option<i64>;
fn sort_by(&self) -> Option<&str>;
fn reverse_sort(&self) -> Option<bool>;
}
pub struct PaginatedResponse<T> {
pub items: Option<Vec<T>>,
pub has_more: bool,
}
pub trait Paginatable: Clone + Sized {
type ListError: Debug;
type Params: PaginationParams + Clone;
fn fetch_page(
config: &apis::configuration::Configuration,
params: &Self::Params,
limit: i64,
) -> Result<PaginatedResponse<Self>, apis::Error<Self::ListError>>;
}
pub struct PaginatedIterator<T: Paginatable> {
config: apis::configuration::Configuration,
params: T::Params,
remaining_limit: i64,
initial_limit: i64,
current_page: std::vec::IntoIter<T>,
finished: bool,
}
impl<T: Paginatable> PaginatedIterator<T> {
pub fn new(
config: apis::configuration::Configuration,
params: T::Params,
initial_limit: Option<i64>,
) -> Self {
let remaining_limit = params.limit().unwrap_or(i64::MAX);
Self {
config,
params,
remaining_limit,
initial_limit: initial_limit.unwrap_or(10_000),
current_page: Vec::new().into_iter(),
finished: false,
}
}
fn fetch_next_page(&mut self) -> Result<bool, apis::Error<T::ListError>> {
if self.finished || (self.remaining_limit != i64::MAX && self.remaining_limit <= 0) {
return Ok(false);
}
let page_limit = std::cmp::min(self.remaining_limit, self.initial_limit);
let response = T::fetch_page(&self.config, &self.params, page_limit)?;
if let Some(items) = response.items {
let items_to_take = if self.remaining_limit == i64::MAX {
items.len()
} else {
std::cmp::min(items.len() as i64, self.remaining_limit) as usize
};
let taken_items: Vec<T> = items.into_iter().take(items_to_take).collect();
if self.remaining_limit != i64::MAX {
self.remaining_limit -= taken_items.len() as i64;
}
let new_offset = self.params.offset() + taken_items.len() as i64;
self.params.set_offset(new_offset);
self.current_page = taken_items.into_iter();
if !response.has_more || (self.remaining_limit != i64::MAX && self.remaining_limit <= 0)
{
self.finished = true;
}
Ok(true)
} else {
self.finished = true;
Ok(false)
}
}
}
impl<T: Paginatable> Iterator for PaginatedIterator<T> {
type Item = Result<T, apis::Error<T::ListError>>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(item) = self.current_page.next() {
return Some(Ok(item));
}
if !self.finished {
match self.fetch_next_page() {
Ok(true) => self.current_page.next().map(Ok),
Ok(false) => None,
Err(e) => Some(Err(e)),
}
} else {
None
}
}
}
pub fn paginate<T: Paginatable>(
config: &apis::configuration::Configuration,
params: T::Params,
) -> Result<Vec<T>, apis::Error<T::ListError>> {
let initial_limit = params.limit().unwrap_or(10_000);
let iter = PaginatedIterator::<T>::new(config.clone(), params, Some(initial_limit));
iter.collect()
}