cqrs_rust_lib/read/
paged.rs1use serde::{Deserialize, Serialize};
2#[cfg(feature = "utoipa")]
3use utoipa::ToSchema;
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14#[cfg_attr(feature = "utoipa", derive(ToSchema))]
15#[serde(rename_all = "camelCase")]
16pub struct Paged<T> {
17 pub items: Vec<T>,
18 pub total: i64,
19 pub skip: i64,
21 pub limit: i64,
23 pub page: i64,
25 pub page_size: i64,
27}
28
29impl<T> Paged<T> {
30 #[must_use]
33 pub fn new(items: Vec<T>, total: i64, skip: i64, limit: i64) -> Self {
34 Self {
35 items,
36 total,
37 skip,
38 limit,
39 page: if limit > 0 { (skip / limit).abs() } else { 0 },
40 page_size: limit,
41 }
42 }
43
44 #[must_use]
46 pub fn map<U, F: FnMut(T) -> U>(self, f: F) -> Paged<U> {
47 Paged {
48 items: self.items.into_iter().map(f).collect(),
49 total: self.total,
50 skip: self.skip,
51 limit: self.limit,
52 page: self.page,
53 page_size: self.page_size,
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use super::*;
61
62 #[test]
63 fn derives_page_from_skip_and_limit() {
64 let p = Paged::new(vec![1, 2, 3], 137, 20, 10);
65 assert_eq!(p.skip, 20);
66 assert_eq!(p.limit, 10);
67 assert_eq!(p.page, 2);
68 assert_eq!(p.page_size, 10);
69 }
70
71 #[test]
72 fn keeps_exact_skip_when_not_a_multiple_of_limit() {
73 let p = Paged::new(vec![1], 137, 25, 10);
74 assert_eq!(p.skip, 25);
75 assert_eq!(p.limit, 10);
76 assert_eq!(p.page, 2);
78 }
79
80 #[test]
81 fn zero_limit_does_not_divide_by_zero() {
82 let p = Paged::new(Vec::<i32>::new(), 0, 0, 0);
83 assert_eq!(p.page, 0);
84 assert_eq!(p.page_size, 0);
85 }
86
87 #[test]
88 fn map_preserves_counters() {
89 let p = Paged::new(vec![1, 2], 7, 5, 2).map(|i| i.to_string());
90 assert_eq!(p.items, vec!["1".to_string(), "2".to_string()]);
91 assert_eq!(p.total, 7);
92 assert_eq!(p.skip, 5);
93 assert_eq!(p.limit, 2);
94 assert_eq!(p.page, 2);
95 assert_eq!(p.page_size, 2);
96 }
97}