toko-feed-cli 0.3.3

Operator CLI for Toko Feed canister ingestion and catalog queries
Documentation
//! Shared bounded traversal for CLI cursor-based pagination.

use super::CliError;

/// Collect bounded cursor pages while delegating cursor semantics to the
/// caller. ULID, compound history, and comparison-key cursors deliberately use
/// different validators.
pub fn collect_pages<T, C>(
    initial_cursor: Option<C>,
    limit: u16,
    max_pages: usize,
    mut fetch: impl FnMut(Option<C>, u16) -> Result<(Vec<T>, Option<C>), CliError>,
    mut validate_next: impl FnMut(Option<&C>, &C) -> Result<(), CliError>,
) -> Result<(Vec<T>, usize), CliError>
where
    C: Clone,
{
    let mut cursor = initial_cursor;
    let mut items = Vec::new();
    let mut pages = 0usize;
    loop {
        enforce_page_budget(pages, max_pages)?;
        let (page_items, next_cursor) = fetch(cursor.clone(), limit)?;
        pages += 1;
        items.extend(page_items);
        let Some(next_cursor) = next_cursor else {
            return Ok((items, pages));
        };
        validate_next(cursor.as_ref(), &next_cursor)?;
        cursor = Some(next_cursor);
    }
}

const fn enforce_page_budget(pages: usize, maximum: usize) -> Result<(), CliError> {
    if pages == maximum {
        Err(CliError::PaginationLimit(maximum))
    } else {
        Ok(())
    }
}