use serde::{Deserialize, Serialize};
use sqlx::Row;
use crate::{
any_struct::FromAnyRow,
database::Connection,
model::Model,
query_builder::QueryBuilder,
AnyImpl,
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Paginated<T> {
pub data: Vec<T>,
pub total: i64,
pub page: usize,
pub limit: usize,
pub total_pages: i64,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Pagination {
#[serde(default)]
pub page: usize,
#[serde(default = "default_limit")]
pub limit: usize,
#[serde(default = "default_max_limit", skip_deserializing)]
pub max_limit: usize,
}
fn default_limit() -> usize {
10
}
fn default_max_limit() -> usize {
100
}
impl Default for Pagination {
fn default() -> Self {
Self { page: 0, limit: 10, max_limit: 100 }
}
}
impl Pagination {
pub fn new_with_limit(page: usize, limit: usize, max_limit: usize) -> Self {
let mut f_limit = limit;
if f_limit > max_limit {
f_limit = max_limit;
}
Self { page, limit: f_limit, max_limit }
}
pub fn new(page: usize, limit: usize) -> Self {
Self::new_with_limit(page, limit, 100)
}
pub fn apply<T, E>(mut self, query: QueryBuilder<T, E>) -> QueryBuilder<T, E>
where
T: Model + Send + Sync + Unpin + AnyImpl,
E: Connection + Send,
{
if self.limit > self.max_limit {
self.limit = self.max_limit;
}
query.limit(self.limit).offset(self.page * self.limit)
}
pub async fn paginate<T, E, R>(self, mut query: QueryBuilder<T, E>) -> Result<Paginated<R>, sqlx::Error>
where
T: Model + Send + Sync + Unpin + AnyImpl,
E: Connection + Send,
R: FromAnyRow + AnyImpl + Send + Unpin,
{
let original_select = query.select_columns.clone();
let original_order = query.order_clauses.clone();
let _original_limit = query.limit;
let _original_offset = query.offset;
query.select_columns = vec!["COUNT(*)".to_string()];
query.order_clauses.clear();
query.limit = None;
query.offset = None;
let count_sql = query.to_sql();
let mut args = sqlx::any::AnyArguments::default();
let mut arg_counter = 1;
let mut dummy_query = String::new(); for clause in &query.where_clauses {
clause(&mut dummy_query, &mut args, &query.driver, &mut arg_counter);
}
if !query.having_clauses.is_empty() {
for clause in &query.having_clauses {
clause(&mut dummy_query, &mut args, &query.driver, &mut arg_counter);
}
}
let count_row = query.tx.fetch_one(&count_sql, args).await?;
let total: i64 = count_row.try_get(0)?;
query.select_columns = original_select;
query.order_clauses = original_order;
query.limit = Some(self.limit);
query.offset = Some(self.page * self.limit);
let data = query.scan::<R>().await?;
let total_pages = (total as f64 / self.limit as f64).ceil() as i64;
Ok(Paginated { data, total, page: self.page, limit: self.limit, total_pages })
}
pub async fn paginate_as<T, E, R>(self, mut query: QueryBuilder<T, E>) -> Result<Paginated<R>, sqlx::Error>
where
T: Model + Send + Sync + Unpin + AnyImpl,
E: Connection + Send,
R: FromAnyRow + AnyImpl + Send + Unpin,
{
let original_select = query.select_columns.clone();
let original_order = query.order_clauses.clone();
let _original_limit = query.limit;
let _original_offset = query.offset;
query.select_columns = vec!["COUNT(*)".to_string()];
query.order_clauses.clear();
query.limit = None;
query.offset = None;
let count_sql = query.to_sql();
let mut args = sqlx::any::AnyArguments::default();
let mut arg_counter = 1;
let mut dummy_query = String::new();
for clause in &query.where_clauses {
clause(&mut dummy_query, &mut args, &query.driver, &mut arg_counter);
}
if !query.having_clauses.is_empty() {
for clause in &query.having_clauses {
clause(&mut dummy_query, &mut args, &query.driver, &mut arg_counter);
}
}
let count_row = query.tx.fetch_one(&count_sql, args).await?;
let total: i64 = count_row.try_get(0)?;
query.select_columns = original_select;
query.order_clauses = original_order;
query.limit = Some(self.limit);
query.offset = Some(self.page * self.limit);
let data = query.scan_as::<R>().await?;
let total_pages = (total as f64 / self.limit as f64).ceil() as i64;
Ok(Paginated { data, total, page: self.page, limit: self.limit, total_pages })
}
}