Skip to main content

api_tools/value_objects/
pagination.rs

1//! Pagination value object representation
2
3/// Pagination min limit
4pub const PAGINATION_MIN_LIMIT: u32 = 10;
5
6/// Pagination max limit
7pub const PAGINATION_MAX_LIMIT: u32 = 500;
8
9/// Pagination default limit
10pub const PAGINATION_DEFAULT_LIMIT: u32 = 200;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
13pub struct Pagination {
14    page: u32,
15    limit: u32,
16    max_limit: Option<u32>,
17}
18
19impl Pagination {
20    /// Create new pagination
21    ///
22    /// `max_limit` is optional and will be clamped between `PAGINATION_MIN_LIMIT` and `PAGINATION_MAX_LIMIT`
23    ///
24    /// # Examples
25    ///
26    /// ```
27    /// use api_tools::value_objects::pagination::{Pagination, PAGINATION_MAX_LIMIT, PAGINATION_MIN_LIMIT};
28    ///
29    /// let pagination = Pagination::new(1, 100, None);
30    /// assert_eq!(pagination.page(), 1);
31    /// assert_eq!(pagination.limit(), 100);
32    ///
33    /// // Invalid page
34    /// let pagination = Pagination::new(0, 100, None);
35    /// assert_eq!(pagination.page(), 1);
36    /// assert_eq!(pagination.limit(), 100);
37    ///
38    /// // Limit too small
39    /// let pagination = Pagination::new(2, 10, None);
40    /// assert_eq!(pagination.page(), 2);
41    /// assert_eq!(pagination.limit(), PAGINATION_MIN_LIMIT);
42    ///
43    /// // Limit too big
44    /// let pagination = Pagination::new(2, 1_000, None);
45    /// assert_eq!(pagination.page(), 2);
46    /// assert_eq!(pagination.limit(), PAGINATION_MAX_LIMIT);
47    ///
48    /// // Limit too big and max limit greater than max
49    /// let pagination = Pagination::new(2, 1_000, Some(800));
50    /// assert_eq!(pagination.page(), 2);
51    /// assert_eq!(pagination.limit(), PAGINATION_MAX_LIMIT);
52    /// ```
53    pub fn new(page: u32, limit: u32, max_limit: Option<u32>) -> Self {
54        let page = if page == 0 { 1 } else { page };
55
56        let mut max = max_limit.unwrap_or(PAGINATION_MAX_LIMIT);
57
58        if max > PAGINATION_MAX_LIMIT {
59            max = PAGINATION_MAX_LIMIT;
60        }
61
62        let limit = if limit > max {
63            max
64        } else if limit < PAGINATION_MIN_LIMIT {
65            PAGINATION_MIN_LIMIT
66        } else {
67            limit
68        };
69
70        Self { page, limit, max_limit }
71    }
72
73    /// Get page
74    pub fn page(&self) -> u32 {
75        self.page
76    }
77
78    /// Get limit
79    pub fn limit(&self) -> u32 {
80        self.limit
81    }
82
83    /// Set a max limit (between `PAGINATION_MIN_LIMIT` and `PAGINATION_MAX_LIMIT`)
84    pub fn set_max_limit(&mut self, max_limit: u32) {
85        let max_limit = max_limit.clamp(PAGINATION_MIN_LIMIT, PAGINATION_MAX_LIMIT);
86        self.max_limit = Some(max_limit);
87    }
88}
89
90impl Default for Pagination {
91    /// Default pagination
92    fn default() -> Self {
93        let default = if PAGINATION_DEFAULT_LIMIT > PAGINATION_MAX_LIMIT {
94            PAGINATION_MAX_LIMIT
95        } else {
96            PAGINATION_DEFAULT_LIMIT
97        };
98        Self::new(1, default, None)
99    }
100}
101
102/// Pagination for response
103#[derive(Debug, Clone, PartialEq)]
104
105pub struct PaginationResponse {
106    pub page: u32,
107    pub limit: u32,
108    pub total: i64,
109}
110
111impl PaginationResponse {
112    /// Create a new pagination response
113    pub fn new(page: u32, limit: u32, total: i64) -> Self {
114        Self { page, limit, total }
115    }
116}
117
118#[cfg(test)]
119mod test {
120    use super::*;
121
122    #[test]
123    fn test_set_max_limit() {
124        let mut pagination = Pagination::default();
125        assert_eq!(pagination.max_limit, None);
126
127        pagination.set_max_limit(100);
128        assert_eq!(pagination.max_limit, Some(100));
129
130        pagination.set_max_limit(300);
131        assert_eq!(pagination.max_limit, Some(300));
132
133        pagination.set_max_limit(5);
134        assert_eq!(pagination.max_limit, Some(PAGINATION_MIN_LIMIT));
135
136        pagination.set_max_limit(600);
137        assert_eq!(pagination.max_limit, Some(PAGINATION_MAX_LIMIT));
138    }
139
140    #[test]
141    fn test_default() {
142        let pagination = Pagination::default();
143        assert_eq!(pagination.page(), 1);
144        assert_eq!(pagination.limit(), PAGINATION_DEFAULT_LIMIT);
145        assert_eq!(pagination.max_limit, None);
146    }
147
148    #[test]
149    fn new_clamps_zero_page_to_one() {
150        let p = Pagination::new(0, 100, None);
151        assert_eq!(p.page(), 1);
152    }
153
154    #[test]
155    fn new_clamps_limit_below_min_to_pagination_min_limit() {
156        let p = Pagination::new(1, 1, None);
157        assert_eq!(p.limit(), PAGINATION_MIN_LIMIT);
158    }
159
160    #[test]
161    fn new_clamps_limit_above_max_to_pagination_max_limit() {
162        let p = Pagination::new(1, PAGINATION_MAX_LIMIT + 100, None);
163        assert_eq!(p.limit(), PAGINATION_MAX_LIMIT);
164    }
165
166    #[test]
167    fn new_caps_max_limit_argument_at_pagination_max_limit() {
168        // A custom max_limit greater than PAGINATION_MAX_LIMIT must itself be
169        // capped at PAGINATION_MAX_LIMIT before clamping the limit value.
170        let p = Pagination::new(1, 10_000, Some(10_000));
171        assert_eq!(p.limit(), PAGINATION_MAX_LIMIT);
172    }
173
174    #[test]
175    fn new_respects_a_smaller_custom_max_limit() {
176        let p = Pagination::new(1, 1_000, Some(100));
177        assert_eq!(p.limit(), 100);
178    }
179
180    #[test]
181    fn pagination_response_new_stores_all_fields() {
182        let r = PaginationResponse::new(2, 50, 1_234);
183        assert_eq!(r.page, 2);
184        assert_eq!(r.limit, 50);
185        assert_eq!(r.total, 1_234);
186    }
187}