fakecloud_core/
pagination.rs1pub fn paginate<T: Clone>(
7 items: &[T],
8 next_token: Option<&str>,
9 max_results: usize,
10) -> (Vec<T>, Option<String>) {
11 if max_results == 0 {
12 return (Vec::new(), None);
13 }
14 let offset: usize = next_token.and_then(|s| s.parse().ok()).unwrap_or(0);
15 let page = if offset < items.len() {
16 &items[offset..]
17 } else {
18 &[][..]
19 };
20 let has_more = page.len() > max_results;
21 let result: Vec<T> = page.iter().take(max_results).cloned().collect();
22 let token = if has_more {
23 Some((offset + max_results).to_string())
24 } else {
25 None
26 };
27 (result, token)
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub struct InvalidNextToken;
36
37pub fn paginate_checked<T: Clone>(
42 items: &[T],
43 next_token: Option<&str>,
44 max_results: usize,
45) -> Result<(Vec<T>, Option<String>), InvalidNextToken> {
46 let offset: usize = match next_token {
47 None => 0,
48 Some(tok) => tok.parse().map_err(|_| InvalidNextToken)?,
49 };
50 if max_results == 0 {
51 return Ok((Vec::new(), None));
52 }
53 let page = if offset < items.len() {
54 &items[offset..]
55 } else {
56 &[][..]
57 };
58 let has_more = page.len() > max_results;
59 let result: Vec<T> = page.iter().take(max_results).cloned().collect();
60 let token = if has_more {
61 Some((offset + max_results).to_string())
62 } else {
63 None
64 };
65 Ok((result, token))
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn first_page() {
74 let items: Vec<i32> = (0..10).collect();
75 let (page, token) = paginate(&items, None, 3);
76 assert_eq!(page, vec![0, 1, 2]);
77 assert_eq!(token, Some("3".to_string()));
78 }
79
80 #[test]
81 fn middle_page() {
82 let items: Vec<i32> = (0..10).collect();
83 let (page, token) = paginate(&items, Some("3"), 3);
84 assert_eq!(page, vec![3, 4, 5]);
85 assert_eq!(token, Some("6".to_string()));
86 }
87
88 #[test]
89 fn last_page() {
90 let items: Vec<i32> = (0..10).collect();
91 let (page, token) = paginate(&items, Some("9"), 3);
92 assert_eq!(page, vec![9]);
93 assert_eq!(token, None);
94 }
95
96 #[test]
97 fn exact_page_boundary() {
98 let items: Vec<i32> = (0..6).collect();
99 let (page, token) = paginate(&items, Some("3"), 3);
100 assert_eq!(page, vec![3, 4, 5]);
101 assert_eq!(token, None);
102 }
103
104 #[test]
105 fn offset_beyond_items() {
106 let items: Vec<i32> = (0..3).collect();
107 let (page, token) = paginate(&items, Some("100"), 3);
108 assert!(page.is_empty());
109 assert_eq!(token, None);
110 }
111
112 #[test]
113 fn invalid_token_defaults_to_zero() {
114 let items: Vec<i32> = (0..5).collect();
115 let (page, token) = paginate(&items, Some("not_a_number"), 3);
116 assert_eq!(page, vec![0, 1, 2]);
117 assert_eq!(token, Some("3".to_string()));
118 }
119
120 #[test]
121 fn zero_max_results_returns_empty_page_without_token() {
122 let items: Vec<i32> = (0..5).collect();
126 let (page, token) = paginate(&items, None, 0);
127 assert!(page.is_empty());
128 assert_eq!(token, None);
129 }
130
131 #[test]
132 fn empty_items() {
133 let items: Vec<i32> = vec![];
134 let (page, token) = paginate(&items, None, 10);
135 assert!(page.is_empty());
136 assert_eq!(token, None);
137 }
138
139 #[test]
142 fn checked_none_is_first_page() {
143 let items: Vec<i32> = (0..5).collect();
144 let (page, token) = paginate_checked(&items, None, 3).unwrap();
145 assert_eq!(page, vec![0, 1, 2]);
146 assert_eq!(token, Some("3".to_string()));
147 }
148
149 #[test]
150 fn checked_valid_token_advances() {
151 let items: Vec<i32> = (0..5).collect();
152 let (page, token) = paginate_checked(&items, Some("3"), 3).unwrap();
153 assert_eq!(page, vec![3, 4]);
154 assert_eq!(token, None);
155 }
156
157 #[test]
158 fn checked_garbage_token_is_rejected() {
159 let items: Vec<i32> = (0..5).collect();
160 assert_eq!(
161 paginate_checked(&items, Some("not_a_number"), 3),
162 Err(InvalidNextToken)
163 );
164 }
165
166 #[test]
167 fn checked_negative_token_is_rejected() {
168 let items: Vec<i32> = (0..5).collect();
169 assert_eq!(
170 paginate_checked(&items, Some("-1"), 3),
171 Err(InvalidNextToken)
172 );
173 }
174}