use std::cmp::Ordering;
use candid::CandidType;
use serde::{Deserialize, Serialize};
#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
pub struct QueryPage {
pub page: u64,
pub size: u32,
}
#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
pub enum QueryPageError {
WrongPage,
WrongSize {
size: u32,
max: u32,
}, }
impl std::fmt::Display for QueryPageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
QueryPageError::WrongPage => write!(f, "page can not be 0"),
QueryPageError::WrongSize { size, max } => {
if *size == 0 {
write!(f, "size can not be 0")
} else {
write!(f, "max({max}) < size({size})")
}
}
}
}
}
impl std::error::Error for QueryPageError {}
#[derive(CandidType, Serialize, Deserialize, Debug, Clone)]
pub struct PageData<T> {
pub page: u64,
pub size: u32,
pub total: u64,
pub data: Vec<T>,
}
impl<T: Clone> From<PageData<&T>> for PageData<T> {
fn from(value: PageData<&T>) -> Self {
PageData {
page: value.page,
size: value.size,
total: value.total,
data: value.data.into_iter().cloned().collect(),
}
}
}
impl QueryPage {
#[inline]
pub fn empty<T>(&self) -> PageData<T> {
PageData {
page: self.page,
size: self.size,
total: 0,
data: Vec::new(),
}
}
#[inline]
pub fn check(&self, max: u32) -> Result<(), QueryPageError> {
if self.page == 0 {
return Err(QueryPageError::WrongPage);
}
if self.size == 0 || max < self.size {
return Err(QueryPageError::WrongSize {
size: self.size,
max,
});
}
Ok(())
}
#[inline]
pub fn from_data<T>(&self, total: u64, data: Vec<T>) -> PageData<T> {
PageData {
page: self.page,
size: self.size,
total,
data,
}
}
#[inline]
fn inner_query_by_list<'a, T>(
&self,
list: &'a [T],
max: u32,
) -> Result<Vec<&'a T>, QueryPageError> {
self.check(max)?;
if list.is_empty() {
return Ok(Vec::new());
}
let mut data = Vec::with_capacity(self.size as usize);
let start = ((self.page - 1) * self.size as u64) as usize;
let end = ((self.page) * self.size as u64) as usize;
if end < list.len() {
data = list[start..end].iter().collect();
} else if start < list.len() {
data = list[start..].iter().collect();
}
Ok(data)
}
#[inline]
pub fn query_by_list<'a, T>(
&self,
list: &'a [T],
max: u32,
) -> Result<PageData<&'a T>, QueryPageError> {
let total = list.len() as u64;
let data = self.inner_query_by_list(list, max)?;
Ok(self.from_data(total, data))
}
#[inline]
pub fn query_desc_by_list<'a, T>(
&self,
list: &'a [T],
max: u32,
) -> Result<PageData<&'a T>, QueryPageError> {
let index_list: Vec<usize> = (0..list.len()).rev().collect();
let total = index_list.len() as u64;
let data = self.inner_query_by_list(&index_list, max)?;
let data = data.into_iter().map(|i| &list[*i]).collect::<Vec<_>>();
Ok(self.from_data(total, data))
}
#[inline]
pub fn query_desc_by_list_and_filter<'a, T, F>(
&self,
list: &'a [T],
max: u32,
filter: F, ) -> Result<PageData<&'a T>, QueryPageError>
where
F: Fn(&T) -> bool,
{
let index_list: Vec<usize> = (0..list.len())
.filter(|i| filter(&list[*i]))
.rev()
.collect();
let total = index_list.len() as u64;
let data = self.inner_query_by_list(&index_list, max)?;
let data = data.into_iter().map(|i| &list[*i]).collect::<Vec<_>>();
Ok(self.from_data(total, data))
}
#[inline]
pub fn custom_query_by_list<T, R, Filter, Compare, Transform>(
&self,
list: &[T],
max: u32,
filter: Filter, compare: Compare, transform: Transform, ) -> Result<PageData<R>, QueryPageError>
where
Filter: Fn(&T) -> bool,
Compare: Fn(&T, &T) -> Ordering,
Transform: Fn(&T) -> R,
{
let mut list: Vec<&T> = list.iter().filter(|&item| filter(item)).collect();
list.sort_by(|&a, &b| compare(a, b));
let total = list.len() as u64;
let data = self.inner_query_by_list(&list, max)?;
let data = data.into_iter().map(|t| transform(t)).collect::<Vec<_>>();
Ok(self.from_data(total, data))
}
}