use crate::exception::{Error, Result};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PaginationMetadata {
pub count: usize,
pub next: Option<String>,
pub previous: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PaginatedResponse<T> {
pub count: usize,
pub next: Option<String>,
pub previous: Option<String>,
pub results: Vec<T>,
}
impl<T> PaginatedResponse<T> {
pub fn new(results: Vec<T>, metadata: PaginationMetadata) -> Self {
Self {
count: metadata.count,
next: metadata.next,
previous: metadata.previous,
results,
}
}
}
#[derive(Debug, Clone)]
pub struct Page<T> {
pub object_list: Vec<T>,
pub number: usize,
pub num_pages: usize,
pub count: usize,
pub page_size: usize,
}
impl<T: Clone> Page<T> {
pub fn new(
object_list: Vec<T>,
number: usize,
num_pages: usize,
count: usize,
page_size: usize,
) -> Self {
Self {
object_list,
number,
num_pages,
count,
page_size,
}
}
pub fn start_index(&self) -> usize {
if self.object_list.is_empty() {
0
} else {
(self.number - 1) * self.page_size + 1
}
}
pub fn end_index(&self) -> usize {
if self.object_list.is_empty() {
0
} else {
self.start_index() + self.object_list.len() - 1
}
}
pub fn has_next(&self) -> bool {
self.number < self.num_pages
}
pub fn has_previous(&self) -> bool {
self.number > 1
}
pub fn has_other_pages(&self) -> bool {
self.has_previous() || self.has_next()
}
pub fn next_page_number(&self) -> Result<usize> {
if self.has_next() {
Ok(self.number + 1)
} else {
Err(Error::Validation(
"That page contains no results".to_string(),
))
}
}
pub fn previous_page_number(&self) -> Result<usize> {
if self.has_previous() {
Ok(self.number - 1)
} else {
Err(Error::InvalidPage(
"That page number is less than 1".to_string(),
))
}
}
pub fn len(&self) -> usize {
self.object_list.len()
}
pub fn is_empty(&self) -> bool {
self.object_list.is_empty()
}
pub fn page_range(&self) -> std::ops::RangeInclusive<usize> {
1..=self.num_pages
}
pub fn get_elided_page_range(&self, on_each_side: usize, on_ends: usize) -> Vec<Option<usize>> {
let mut result = Vec::new();
let needed_pages = on_each_side * 2 + 1 + on_ends * 2;
if self.num_pages <= needed_pages {
return (1..=self.num_pages).map(Some).collect();
}
for i in 1..=on_ends {
if i <= self.num_pages {
result.push(Some(i));
}
}
let left_start = self.number.saturating_sub(on_each_side);
if left_start > on_ends + 1 {
result.push(None); }
let middle_start = std::cmp::max(on_ends + 1, left_start);
let middle_end = std::cmp::min(self.num_pages - on_ends, self.number + on_each_side);
for i in middle_start..=middle_end {
if i > on_ends && i <= self.num_pages - on_ends {
result.push(Some(i));
}
}
if middle_end < self.num_pages - on_ends {
result.push(None); }
for i in (self.num_pages - on_ends + 1)..=self.num_pages {
if i > middle_end {
result.push(Some(i));
}
}
result
}
pub fn get(&self, index: usize) -> Option<&T> {
self.object_list.get(index)
}
pub fn get_slice(&self, range: std::ops::Range<usize>) -> Option<&[T]> {
self.object_list.get(range)
}
}
impl<T: Clone> std::ops::Index<usize> for Page<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.object_list[index]
}
}
impl<T: Clone> IntoIterator for Page<T> {
type Item = T;
type IntoIter = std::vec::IntoIter<T>;
fn into_iter(self) -> Self::IntoIter {
self.object_list.into_iter()
}
}
impl<'a, T: Clone> IntoIterator for &'a Page<T> {
type Item = &'a T;
type IntoIter = std::slice::Iter<'a, T>;
fn into_iter(self) -> Self::IntoIter {
self.object_list.iter()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct SchemaParameter {
pub name: String,
pub required: bool,
pub location: String,
pub description: String,
pub schema_type: String,
}
#[async_trait]
pub trait Paginator: Send + Sync {
fn paginate<T: Clone + Send + Sync>(
&self,
items: &[T],
page_param: Option<&str>,
base_url: &str,
) -> Result<PaginatedResponse<T>>;
fn get_schema_parameters(&self) -> Vec<SchemaParameter> {
Vec::new()
}
}
#[async_trait]
pub trait AsyncPaginator: Send + Sync {
async fn apaginate<T: Clone + Send + Sync>(
&self,
items: &[T],
page_param: Option<&str>,
base_url: &str,
) -> Result<PaginatedResponse<T>>;
fn get_schema_parameters(&self) -> Vec<SchemaParameter> {
Vec::new()
}
}