1#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub struct Cursor(u32);
9
10impl Cursor {
11 #[must_use]
13 pub const fn at(position: u32) -> Self {
14 Self(position)
15 }
16
17 #[must_use]
22 pub const fn position(self) -> u32 {
23 self.0
24 }
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct Page<T> {
35 items: Vec<T>,
36 next: Option<Cursor>,
37}
38
39impl<T> Page<T> {
40 #[must_use]
42 pub const fn complete(items: Vec<T>) -> Self {
43 Self { items, next: None }
44 }
45
46 #[must_use]
48 pub const fn partial(items: Vec<T>, next: Cursor) -> Self {
49 Self {
50 items,
51 next: Some(next),
52 }
53 }
54
55 #[must_use]
57 pub fn items(&self) -> &[T] {
58 &self.items
59 }
60
61 #[must_use]
63 pub const fn next_cursor(&self) -> Option<Cursor> {
64 self.next
65 }
66
67 #[must_use]
69 pub const fn has_more(&self) -> bool {
70 self.next.is_some()
71 }
72
73 #[must_use]
75 pub fn len(&self) -> usize {
76 self.items.len()
77 }
78
79 #[must_use]
81 pub fn is_empty(&self) -> bool {
82 self.items.is_empty()
83 }
84
85 #[must_use]
87 pub fn into_items(self) -> Vec<T> {
88 self.items
89 }
90}
91
92impl<T> IntoIterator for Page<T> {
93 type Item = T;
94 type IntoIter = std::vec::IntoIter<T>;
95
96 fn into_iter(self) -> Self::IntoIter {
97 self.items.into_iter()
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn complete_page_has_no_cursor() {
107 let page = Page::complete(vec![1, 2, 3]);
108 assert_eq!(page.len(), 3);
109 assert!(!page.has_more());
110 assert!(page.next_cursor().is_none());
111 }
112
113 #[test]
114 fn partial_page_carries_a_cursor() {
115 let page = Page::partial(vec![1], Cursor::at(4));
116 assert!(page.has_more());
117 assert_eq!(page.next_cursor().unwrap().position(), 4);
118 }
119
120 #[test]
121 fn empty_complete_page_is_exhausted_not_truncated() {
122 let page: Page<u8> = Page::complete(vec![]);
125 assert!(page.is_empty());
126 assert!(!page.has_more());
127 }
128
129 #[test]
130 fn empty_partial_page_still_reports_more() {
131 let page: Page<u8> = Page::partial(vec![], Cursor::at(1));
132 assert!(page.is_empty());
133 assert!(page.has_more());
134 }
135
136 #[test]
137 fn into_items_returns_the_results() {
138 let page = Page::complete(vec![7, 8]);
139 assert_eq!(page.into_items(), vec![7, 8]);
140 }
141
142 #[test]
143 fn page_iterates() {
144 let page = Page::complete(vec![1, 2, 3]);
145 assert_eq!(page.into_iter().sum::<i32>(), 6);
146 }
147}