Skip to main content

fakecloud_core/
pagination.rs

1/// Offset-based pagination helper for AWS list operations.
2///
3/// Parses `next_token` as a numeric offset (defaulting to 0 if `None` or unparseable),
4/// slices `items` starting at that offset, and returns at most `max_results` items
5/// along with an optional next token for the following page.
6pub fn paginate<T: Clone>(
7    items: &[T],
8    next_token: Option<&str>,
9    max_results: usize,
10) -> (Vec<T>, Option<String>) {
11    let max_results = max_results.max(1);
12    let offset: usize = next_token.and_then(|s| s.parse().ok()).unwrap_or(0);
13    let page = if offset < items.len() {
14        &items[offset..]
15    } else {
16        &[][..]
17    };
18    let has_more = page.len() > max_results;
19    let result: Vec<T> = page.iter().take(max_results).cloned().collect();
20    let token = if has_more {
21        Some((offset + max_results).to_string())
22    } else {
23        None
24    };
25    (result, token)
26}
27
28#[cfg(test)]
29mod tests {
30    use super::*;
31
32    #[test]
33    fn first_page() {
34        let items: Vec<i32> = (0..10).collect();
35        let (page, token) = paginate(&items, None, 3);
36        assert_eq!(page, vec![0, 1, 2]);
37        assert_eq!(token, Some("3".to_string()));
38    }
39
40    #[test]
41    fn middle_page() {
42        let items: Vec<i32> = (0..10).collect();
43        let (page, token) = paginate(&items, Some("3"), 3);
44        assert_eq!(page, vec![3, 4, 5]);
45        assert_eq!(token, Some("6".to_string()));
46    }
47
48    #[test]
49    fn last_page() {
50        let items: Vec<i32> = (0..10).collect();
51        let (page, token) = paginate(&items, Some("9"), 3);
52        assert_eq!(page, vec![9]);
53        assert_eq!(token, None);
54    }
55
56    #[test]
57    fn exact_page_boundary() {
58        let items: Vec<i32> = (0..6).collect();
59        let (page, token) = paginate(&items, Some("3"), 3);
60        assert_eq!(page, vec![3, 4, 5]);
61        assert_eq!(token, None);
62    }
63
64    #[test]
65    fn offset_beyond_items() {
66        let items: Vec<i32> = (0..3).collect();
67        let (page, token) = paginate(&items, Some("100"), 3);
68        assert!(page.is_empty());
69        assert_eq!(token, None);
70    }
71
72    #[test]
73    fn invalid_token_defaults_to_zero() {
74        let items: Vec<i32> = (0..5).collect();
75        let (page, token) = paginate(&items, Some("not_a_number"), 3);
76        assert_eq!(page, vec![0, 1, 2]);
77        assert_eq!(token, Some("3".to_string()));
78    }
79
80    #[test]
81    fn zero_max_results_returns_one_item() {
82        let items: Vec<i32> = (0..5).collect();
83        let (page, token) = paginate(&items, None, 0);
84        assert_eq!(page, vec![0]);
85        assert_eq!(token, Some("1".to_string()));
86    }
87
88    #[test]
89    fn empty_items() {
90        let items: Vec<i32> = vec![];
91        let (page, token) = paginate(&items, None, 10);
92        assert!(page.is_empty());
93        assert_eq!(token, None);
94    }
95}