geekorm_core/queries/
pagination.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! # Pagination

use super::pages::Page;
use crate::{GeekConnection, QueryBuilderTrait, TableBuilder};

/// A struct for paginating results
///
/// # Example
///
/// ```no_run
/// # #[cfg(feature = "libsql")] {
/// # use geekorm::prelude::*;
///
/// #[derive(Table, Debug, Default, Clone, serde::Serialize, serde::Deserialize)]
/// pub struct Users {
///     pub id: PrimaryKeyInteger,
///     pub username: String,
///     pub age: i32,
///     pub postcode: Option<String>,
/// }
///
/// pub type UserPage = Pagination<Users>;
///
/// #[tokio::main]
/// async fn main() -> anyhow::Result<()> {
///     let database = libsql::Builder::new_local(":memory:").build().await?;
///     let connection = database.connect().unwrap();
///     
///     // Create a new Page instance
///     let mut page = UserPage::new();
///
///     // Update the page to the next page
///     let results = page.next(&connection).await?;
///     # assert_eq!(page.limit(), 100);
///     # assert_eq!(page.page(), 0);
///
///    # Ok(())
/// }
/// # }
/// ```
pub struct Pagination<T>
where
    T: TableBuilder
        + QueryBuilderTrait
        + serde::Serialize
        + for<'de> serde::Deserialize<'de>
        + Sized,
{
    phantom: std::marker::PhantomData<T>,
    page: Page,
}

impl<T> Pagination<T>
where
    T: TableBuilder
        + QueryBuilderTrait
        + serde::Serialize
        + for<'de> serde::Deserialize<'de>
        + Sized,
{
    /// Create a new Pagination instance
    pub fn new() -> Self {
        Self::default()
    }
    /// Get the current page
    pub fn page(&self) -> u32 {
        self.page.page()
    }
    /// Get the limit
    pub fn limit(&self) -> u32 {
        self.page.limit()
    }
    /// Get the total number of items
    pub fn total(&self) -> u32 {
        self.page.total
    }
    /// Set the total number of items
    pub fn set_total(&mut self, total: u32) {
        self.page.set_total(total);
    }

    /// Get the current page results
    pub async fn get<'a, C>(&mut self, connection: &'a C) -> Result<Vec<T>, crate::Error>
    where
        C: GeekConnection<Connection = C> + 'a,
    {
        // Gets the total number of rows if it hasn't been set
        if self.page.total == 0 {
            self.page
                .set_total(C::row_count(connection, T::query_count().build()?).await? as u32);
        }
        Ok(C::query(connection, T::query_select().page(&self.page).build()?).await?)
    }

    /// Get the next page of results
    pub async fn next<'a, C>(&mut self, connection: &'a C) -> Result<Vec<T>, crate::Error>
    where
        C: GeekConnection<Connection = C> + 'a,
    {
        self.page.next();
        if self.page.max() < self.page.page {
            return Err(crate::Error::PaginationError(
                "Cannot go to next page".to_string(),
            ));
        }
        self.get(connection).await
    }

    /// Get the previous page of results
    pub async fn prev<'a, C>(&mut self, connection: &'a C) -> Result<Vec<T>, crate::Error>
    where
        C: GeekConnection<Connection = C> + 'a,
    {
        if self.page.page == u32::MAX || self.page.page == 0 {
            return Err(crate::Error::PaginationError(
                "Cannot go to previous page".to_string(),
            ));
        }
        self.page.prev();
        self.get(connection).await
    }
}

impl<T> Default for Pagination<T>
where
    T: TableBuilder
        + QueryBuilderTrait
        + serde::Serialize
        + for<'de> serde::Deserialize<'de>
        + Sized,
{
    fn default() -> Self {
        Self {
            phantom: std::marker::PhantomData,
            page: Page {
                page: u32::MAX,
                ..Default::default()
            },
        }
    }
}