summaryrefslogtreecommitdiff
path: root/src/pagination.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/pagination.rs')
-rw-r--r--src/pagination.rs103
1 files changed, 103 insertions, 0 deletions
diff --git a/src/pagination.rs b/src/pagination.rs
new file mode 100644
index 0000000..961b079
--- /dev/null
+++ b/src/pagination.rs
@@ -0,0 +1,103 @@
+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<Self>;
+}
+
+impl<T> SortingAndPaging for T {
+ fn paginate(self, page: i64) -> SortedAndPaginated<Self> {
+ SortedAndPaginated {
+ query: self,
+ sort_by: "".to_string(),
+ sort_direction: "".to_string(),
+ per_page: 100,
+ page,
+ }
+ }
+}
+
+#[derive(Serialize)]
+#[serde(crate = "rocket::serde")]
+pub struct Page<T> {
+ pub data: Vec<T>,
+ pub page_num: i64,
+ pub page_size: i64,
+ pub total_elements: i64,
+}
+
+impl<T> Page<T> {
+ pub fn new(data: Vec<T>, page_num: i64, page_size: i64, total_elements: i64) -> Page<T> {
+ Page {
+ data,
+ page_num,
+ page_size,
+ total_elements,
+ }
+ }
+}
+
+#[derive(Debug, Clone, QueryId)]
+pub struct SortedAndPaginated<T> {
+ query: T,
+ sort_by: String,
+ sort_direction: String,
+ page: i64,
+ per_page: i64,
+}
+
+impl<T> SortedAndPaginated<T> {
+ 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<U>(self, conn: &SqliteConnection) -> QueryResult<Page<U>>
+ where
+ Self: LoadQuery<SqliteConnection, (U, i64)>,
+ {
+ 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<T: Query> Query for SortedAndPaginated<T> {
+ type SqlType = (T::SqlType, BigInt);
+}
+
+impl<T> RunQueryDsl<SqliteConnection> for SortedAndPaginated<T> {}
+
+impl<T> QueryFragment<Sqlite> for SortedAndPaginated<T>
+where
+ T: QueryFragment<Sqlite>,
+{
+ fn walk_ast(&self, mut out: AstPass<Sqlite>) -> 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::<BigInt, _>(&self.per_page)?;
+ out.push_sql(" OFFSET ");
+ let offset = (self.page - 1) * self.per_page;
+ out.push_bind_param::<BigInt, _>(&offset)?;
+ Ok(())
+ }
+}