data_pager/
pager.rs

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        // first page
111        let page = PageInfo {
112            cursor: None,
113            page_size: 10,
114        };
115
116        // assume we got 11 items from db
117        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        // second page
128        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        // third page
140        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}