pub const MAX_PAGE_SIZE: u16 = 1000;
pub fn paginate<T: Clone>(all: &[T], page: u32, page_size: u16) -> (Vec<T>, Option<u32>) {
let page_size = page_size.clamp(1, MAX_PAGE_SIZE) as usize;
let start = (page as usize).saturating_mul(page_size);
let items = all.iter().skip(start).take(page_size).cloned().collect();
let next_page = (start.saturating_add(page_size) < all.len()).then_some(page.saturating_add(1));
(items, next_page)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_empty_listing_has_no_items_and_no_next_page() {
let (items, next_page) = paginate::<u8>(&[], 0, 10);
assert!(items.is_empty());
assert_eq!(next_page, None);
}
#[test]
fn the_last_page_reports_no_next_page() {
let all = [1, 2, 3, 4, 5];
let (items, next_page) = paginate(&all, 0, 3);
assert_eq!(items, vec![1, 2, 3]);
assert_eq!(next_page, Some(1));
let (items, next_page) = paginate(&all, 1, 3);
assert_eq!(items, vec![4, 5]);
assert_eq!(next_page, None);
}
#[test]
fn an_exactly_full_final_page_ends_the_listing() {
let (items, next_page) = paginate(&[1, 2, 3, 4], 1, 2);
assert_eq!(items, vec![3, 4]);
assert_eq!(next_page, None);
}
#[test]
fn a_zero_page_size_is_clamped_to_one() {
let (items, next_page) = paginate(&[1, 2, 3], 0, 0);
assert_eq!(items, vec![1]);
assert_eq!(next_page, Some(1));
}
#[test]
fn an_oversized_page_size_is_clamped_to_the_maximum() {
let all: Vec<u32> = (0..MAX_PAGE_SIZE as u32 + 5).collect();
let (items, next_page) = paginate(&all, 0, u16::MAX);
assert_eq!(items.len(), MAX_PAGE_SIZE as usize);
assert_eq!(next_page, Some(1));
}
#[test]
fn a_page_past_the_end_is_empty() {
let (items, next_page) = paginate(&[1, 2, 3], 99, 10);
assert!(items.is_empty());
assert_eq!(next_page, None);
}
}