1use std::{cmp::max, collections::VecDeque};
2
3#[derive(Debug, Clone, Default, PartialEq, Eq)]
4pub struct PageInfo {
5 pub cursor: Option<u64>,
6 pub page_size: u64,
7}
8
9#[derive(Debug, Clone, Default, PartialEq, Eq)]
10pub struct Pager {
11 pub prev: Option<u64>,
12 pub next: Option<u64>,
13 pub total: Option<u64>,
14}
15pub trait Paginator: Sized {
16 fn get_pager<T: Container>(&self, data: &mut T) -> Pager;
17 fn next_page(&self, pager: &Pager) -> Option<Self>;
18 fn prev_page(&self, pager: &Pager) -> Option<Self>;
19}
20
21pub trait Container {
22 fn pop(&mut self);
23 fn len(&self) -> usize;
24 fn is_empty(&self) -> bool {
25 self.len() == 0
26 }
27}
28
29impl<T> Container for VecDeque<T> {
30 fn pop(&mut self) {
31 self.pop_back();
32 }
33
34 fn len(&self) -> usize {
35 self.len()
36 }
37}
38
39impl<T> Container for Vec<T> {
40 fn pop(&mut self) {
41 self.pop();
42 }
43
44 fn len(&self) -> usize {
45 self.len()
46 }
47}
48
49impl Paginator for PageInfo {
50 fn get_pager<T: Container>(&self, data: &mut T) -> Pager {
51 let prev = match self.cursor {
52 Some(v) if v > 0 => Some(max(0, v - self.page_size)),
53 _ => None,
54 };
55
56 let has_next = data.len() as u64 > self.page_size;
57 let next = if has_next {
58 data.pop();
59 Some(self.cursor.unwrap_or(0) + self.page_size)
60 } else {
61 None
62 };
63
64 Pager {
65 prev,
66 next,
67 total: None,
68 }
69 }
70
71 fn next_page(&self, pager: &Pager) -> Option<Self> {
72 if pager.next.is_some() {
73 Some(PageInfo {
74 cursor: pager.next,
75 page_size: self.page_size,
76 })
77 } else {
78 None
79 }
80 }
81
82 fn prev_page(&self, pager: &Pager) -> Option<Self> {
83 if pager.prev.is_some() {
84 Some(PageInfo {
85 cursor: pager.prev,
86 page_size: self.page_size,
87 })
88 } else {
89 None
90 }
91 }
92}
93
94#[cfg(test)]
95pub mod pager_test_utils {
96 use std::collections::VecDeque;
97 pub struct TestId(u64);
98
99 pub fn generate_test_ids(start: u64, end: u64) -> VecDeque<TestId> {
100 (start..=end).map(TestId).collect()
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107
108 #[test]
109 fn paginator_should_work() {
110 let page = PageInfo {
112 cursor: None,
113 page_size: 10,
114 };
115
116 let mut items = pager_test_utils::generate_test_ids(1, 11);
118 let pager = page.get_pager(&mut items);
119 assert!(pager.prev.is_none());
120 assert_eq!(pager.next, Some(10));
121
122 {
123 let prev_page = page.prev_page(&pager);
124 assert!(prev_page.is_none());
125 }
126
127 let page = page.next_page(&pager).unwrap();
129 let mut items = pager_test_utils::generate_test_ids(11, 21);
130 let pager = page.get_pager(&mut items);
131 assert_eq!(pager.prev, Some(0));
132 assert_eq!(pager.next, Some(20));
133
134 {
135 let prev_page = page.prev_page(&pager);
136 assert_eq!(prev_page.unwrap().cursor, Some(0));
137 }
138
139 let page = page.next_page(&pager).unwrap();
141 let mut items = pager_test_utils::generate_test_ids(21, 25);
142 let pager = page.get_pager(&mut items);
143 assert_eq!(pager.prev, Some(10));
144 assert!(pager.next.is_none());
145
146 {
147 let prev_page = page.prev_page(&pager);
148 assert_eq!(prev_page.unwrap().cursor, Some(10));
149 }
150 }
151}