Skip to main content

anycms_core/
pagination.rs

1use serde::{Deserialize, Serialize};
2
3/// Pagination metadata for list responses
4#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
5#[cfg_attr(not(feature = "snake-case"), serde(rename_all = "camelCase"))]
6#[cfg_attr(feature = "snake-case", serde(rename_all = "snake_case"))]
7pub struct ResultPagination {
8    pub total: i64,
9    pub page: i64,
10    pub page_size: i64,
11    /// Computed total number of pages
12    pub total_pages: i64,
13    /// Whether there is a next page
14    pub has_next_page: bool,
15    /// Whether there is a previous page
16    pub has_prev_page: bool,
17}
18
19impl Default for ResultPagination {
20    fn default() -> Self {
21        Self::new(0, 1, 10)
22    }
23}
24
25// Legacy compatibility: keep current_page accessible as alias for page
26impl ResultPagination {
27    /// Legacy alias for `page` field (deprecated, use `page` instead)
28    #[deprecated(since = "0.5.0", note = "Use `page` field instead")]
29    pub fn current_page(&self) -> i64 {
30        self.page
31    }
32}
33
34impl ResultPagination {
35    /// Create new pagination metadata
36    pub fn new(total: i64, page: i64, page_size: i64) -> Self {
37        let total_pages = if page_size <= 0 {
38            0
39        } else {
40            (total + page_size - 1) / page_size
41        };
42        let has_next_page = page < total_pages;
43        let has_prev_page = page > 1;
44        Self {
45            total,
46            page,
47            page_size,
48            total_pages,
49            has_next_page,
50            has_prev_page,
51        }
52    }
53
54    /// Total number of pages
55    #[inline]
56    pub fn total_pages(&self) -> i64 {
57        self.total_pages
58    }
59
60    /// Whether there is a next page
61    #[inline]
62    pub fn has_next(&self) -> bool {
63        self.has_next_page
64    }
65
66    /// Whether there is a previous page
67    #[inline]
68    pub fn has_prev(&self) -> bool {
69        self.has_prev_page
70    }
71
72    /// Calculate the offset for database queries: `(page - 1) * page_size`
73    ///
74    /// Useful for SQL `OFFSET` clause or ORM pagination.
75    ///
76    /// # Example
77    /// ```
78    /// use anycms_core::ResultPagination;
79    /// let p = ResultPagination::new(100, 3, 10);
80    /// assert_eq!(p.offset(), 20);
81    /// ```
82    #[inline]
83    pub fn offset(&self) -> i64 {
84        self.page.saturating_sub(1) * self.page_size
85    }
86
87    /// Get the limit for database queries (alias for `page_size`)
88    ///
89    /// Useful for SQL `LIMIT` clause or ORM pagination.
90    #[inline]
91    pub fn limit(&self) -> i64 {
92        self.page_size
93    }
94
95    /// Whether this is the first page (page <= 1)
96    #[inline]
97    pub fn is_first_page(&self) -> bool {
98        self.page <= 1
99    }
100
101    /// Whether this is the last page (page >= total_pages)
102    #[inline]
103    pub fn is_last_page(&self) -> bool {
104        self.page >= self.total_pages
105    }
106
107    /// Get the next page number, if available
108    #[inline]
109    pub fn next_page(&self) -> Option<i64> {
110        if self.has_next_page {
111            Some(self.page + 1)
112        } else {
113            None
114        }
115    }
116
117    /// Get the previous page number, if available
118    #[inline]
119    pub fn prev_page(&self) -> Option<i64> {
120        if self.has_prev_page {
121            Some(self.page - 1)
122        } else {
123            None
124        }
125    }
126}