use diesel::{query_dsl::methods::LoadQuery, PgConnection, QueryResult, RunQueryDsl, query_builder::{QueryFragment, AstPass, Query, QueryId}, pg::Pg, sql_types::BigInt};
pub trait Paginate: Sized {
fn paginate(self, page: i64) -> Paginated<Self>;
}
impl<T> Paginate for T {
fn paginate(self, page: i64) -> Paginated<Self> {
Paginated {
query: self,
per_page: DEFAULT_PER_PAGE,
page,
offset: (page - 1) * DEFAULT_PER_PAGE,
is_sub_query: false,
is_big_table: false,
}
}
}
const DEFAULT_PER_PAGE: i64 = 10;
#[derive(Debug, Clone, Copy, QueryId)]
pub struct Paginated<T> {
pub query: T,
pub page: i64,
pub per_page: i64,
pub offset: i64,
pub is_sub_query: bool,
pub is_big_table: bool,
}
impl<T> Paginated<T> {
pub fn per_page(self, per_page: i64) -> Self {
Paginated {
per_page,
offset: (self.page - 1) * per_page,
..self
}
}
pub fn load_and_count_pages<'a, U>(self, conn: &mut PgConnection) -> QueryResult<(Vec<U>, i64)>
where
Self: LoadQuery<'a, PgConnection, (U, i64)>,
{
let per_page = self.per_page;
let results = self.load::<(U, i64)>(conn)?;
let total = results.get(0).map(|x| x.1).unwrap_or(0);
let records = results.into_iter().map(|x| x.0).collect();
let total_pages = (total as f64 / per_page as f64).ceil() as i64;
Ok((records, total_pages))
}
pub fn load_and_count_pages_total<U>(self,conn: &mut PgConnection) -> QueryResult<(Vec<U>, i64, i64)>
where
Self: for<'a> LoadQuery<'a, PgConnection, (U, i64)>,
{
let per_page = self.per_page;
let results = self.load::<(U, i64)>(conn)?;
let total = results.get(0).map(|x| x.1).unwrap_or(0);
let records = results.into_iter().map(|x| x.0).collect();
let total_pages = (total as f64 / per_page as f64).ceil() as i64;
Ok((records, total_pages,total))
}
}
impl<T> QueryFragment<Pg> for Paginated<T>
where
T: QueryFragment<Pg>,
{
fn walk_ast<'b>(&'b self, mut out: AstPass<'_, 'b, Pg>) -> QueryResult<()> {
out.push_sql("SELECT *, COUNT(*) OVER () FROM (");
self.query.walk_ast(out.reborrow())?;
out.push_sql(") as paged_query_with LIMIT ");
out.push_bind_param::<BigInt, _>(&self.per_page)?;
out.push_sql(" OFFSET ");
out.push_bind_param::<BigInt, _>(&self.offset)?;
Ok(())
}
}
impl<T: Query> Query for Paginated<T> {
type SqlType = (T::SqlType, BigInt);
}
impl<T> RunQueryDsl<PgConnection> for Paginated<T> {}