use diesel::prelude::*; use diesel::query_builder::*; use diesel::query_dsl::methods::LoadQuery; use diesel::sql_types::BigInt; use diesel::sqlite::Sqlite; use rocket::serde::Serialize; pub trait SortingAndPaging: Sized { fn paginate(self, page: i64) -> SortedAndPaginated; } impl SortingAndPaging for T { fn paginate(self, page: i64) -> SortedAndPaginated { SortedAndPaginated { query: self, sort_by: "".to_string(), sort_direction: "".to_string(), per_page: 100, page, } } } #[derive(Serialize)] #[serde(crate = "rocket::serde")] pub struct Page { pub data: Vec, pub page_num: i64, pub page_size: i64, pub total_elements: i64, } impl Page { pub fn new(data: Vec, page_num: i64, page_size: i64, total_elements: i64) -> Page { Page { data, page_num, page_size, total_elements, } } } #[derive(Debug, Clone, QueryId)] pub struct SortedAndPaginated { query: T, sort_by: String, sort_direction: String, page: i64, per_page: i64, } impl SortedAndPaginated { pub fn per_page(self, per_page: i64) -> Self { SortedAndPaginated { per_page, ..self } } pub fn sort(self, sort_by: String, sort_direction: String) -> Self { SortedAndPaginated { sort_by, sort_direction, ..self } } pub fn load_and_count_items(self, conn: &SqliteConnection) -> QueryResult> where Self: LoadQuery, { let page = self.page; 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(); Ok(Page::new(records, page, per_page, total)) } } impl Query for SortedAndPaginated { type SqlType = (T::SqlType, BigInt); } impl RunQueryDsl for SortedAndPaginated {} impl QueryFragment for SortedAndPaginated where T: QueryFragment, { fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { out.push_sql("SELECT *, COUNT(*) OVER () FROM ("); self.query.walk_ast(out.reborrow())?; out.push_sql(") t "); if &self.sort_by.as_str().len() > &0 { out.push_sql(format!(" ORDER BY {} {}", &self.sort_by, &self.sort_direction).as_str()); } out.push_sql(" LIMIT "); out.push_bind_param::(&self.per_page)?; out.push_sql(" OFFSET "); let offset = (self.page - 1) * self.per_page; out.push_bind_param::(&offset)?; Ok(()) } }