geekorm_core/queries/
pagination.rs

1//! # Pagination
2
3use super::pages::Page;
4use crate::{GeekConnection, QueryBuilderTrait, TableBuilder};
5
6/// A struct for paginating results
7///
8/// # Example
9///
10/// ```no_run
11/// # #[cfg(feature = "libsql")] {
12/// # use geekorm::prelude::*;
13///
14/// #[derive(Table, Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
15/// pub struct Users {
16///     pub id: PrimaryKeyInteger,
17///     pub username: String,
18///     pub age: i32,
19///     pub postcode: Option<String>,
20/// }
21///
22/// pub type UserPage = Pagination<Users>;
23///
24/// #[tokio::main]
25/// async fn main() -> anyhow::Result<()> {
26///     let database = libsql::Builder::new_local(":memory:").build().await?;
27///     let connection = database.connect().unwrap();
28///     
29///     // Create a new Page instance
30///     let mut page = UserPage::new();
31///
32///     // Update the page to the next page
33///     let results = page.next(&connection).await?;
34///     # assert_eq!(page.limit(), 100);
35///     # assert_eq!(page.page(), 0);
36///
37///    # Ok(())
38/// }
39/// # }
40/// ```
41pub struct Pagination<T>
42where
43    T: TableBuilder
44        + QueryBuilderTrait
45        + serde::Serialize
46        + for<'de> serde::Deserialize<'de>
47        + Sized,
48{
49    phantom: std::marker::PhantomData<T>,
50    page: Page,
51}
52
53impl<T> Pagination<T>
54where
55    T: TableBuilder
56        + QueryBuilderTrait
57        + serde::Serialize
58        + for<'de> serde::Deserialize<'de>
59        + Sized,
60{
61    /// Create a new Pagination instance
62    pub fn new() -> Self {
63        Self::default()
64    }
65    /// Get the current page
66    pub fn page(&self) -> u32 {
67        self.page.page()
68    }
69    /// Get the limit
70    pub fn limit(&self) -> u32 {
71        self.page.limit()
72    }
73    /// Get the total number of items
74    pub fn total(&self) -> u32 {
75        self.page.total
76    }
77    /// Set the total number of items
78    pub fn set_total(&mut self, total: u32) {
79        self.page.set_total(total);
80    }
81
82    /// Get the current page results
83    pub async fn get<'a, C>(&mut self, connection: &'a C) -> Result<Vec<T>, crate::Error>
84    where
85        C: GeekConnection<Connection = C> + 'a,
86    {
87        // Gets the total number of rows if it hasn't been set
88        if self.page.total == 0 {
89            self.page
90                .set_total(C::row_count(connection, T::query_count().build()?).await? as u32);
91        }
92        C::query(connection, T::query_select().page(&self.page).build()?).await
93    }
94
95    /// Get the next page of results
96    pub async fn next<'a, C>(&mut self, connection: &'a C) -> Result<Vec<T>, crate::Error>
97    where
98        C: GeekConnection<Connection = C> + 'a,
99    {
100        self.page.next();
101        if self.page.max() < self.page.page {
102            return Err(crate::Error::PaginationError(
103                "Cannot go to next page".to_string(),
104            ));
105        }
106        self.get(connection).await
107    }
108
109    /// Get the previous page of results
110    pub async fn prev<'a, C>(&mut self, connection: &'a C) -> Result<Vec<T>, crate::Error>
111    where
112        C: GeekConnection<Connection = C> + 'a,
113    {
114        if self.page.page == u32::MAX || self.page.page == 0 {
115            return Err(crate::Error::PaginationError(
116                "Cannot go to previous page".to_string(),
117            ));
118        }
119        self.page.prev();
120        self.get(connection).await
121    }
122}
123
124impl<T> Default for Pagination<T>
125where
126    T: TableBuilder
127        + QueryBuilderTrait
128        + serde::Serialize
129        + for<'de> serde::Deserialize<'de>
130        + Sized,
131{
132    fn default() -> Self {
133        Self {
134            phantom: std::marker::PhantomData,
135            page: Page {
136                page: u32::MAX,
137                ..Default::default()
138            },
139        }
140    }
141}