Skip to main content

babelforce_manager_sdk/
http.rs

1//! Pagination helper for v2 list endpoints, which return
2//! `{ items: Vec<T>, pagination: Pagination { pages, current, … } }`.
3use crate::error::ManagerError;
4use crate::retry::{with_retry, RetryPolicy};
5
6/// One fetched page of a v2 list endpoint.
7#[derive(Debug, Clone)]
8pub struct Page<T> {
9    /// The items on this page.
10    pub items: Vec<T>,
11    /// The 1-based page this response is (the API's `pagination.current`).
12    pub page: i32,
13    /// Total number of pages.
14    pub pages: i32,
15    /// Total items across all pages, when the endpoint reports it.
16    pub total: Option<i32>,
17}
18
19/// Fetch exactly one page. `fetch()` performs the single page request and returns
20/// `(items, total_pages, current_page, total_items)`; retried like any idempotent read.
21/// The single-page sibling of [`collect_all`] — for callers that surface pagination
22/// instead of flattening it.
23pub(crate) async fn fetch_page<T, E, F, Fut, M>(
24    retry: &RetryPolicy,
25    map_err: M,
26    fetch: F,
27) -> Result<Page<T>, ManagerError>
28where
29    M: Fn(E) -> ManagerError,
30    F: Fn() -> Fut,
31    Fut: std::future::Future<Output = Result<(Vec<T>, i32, i32, Option<i32>), E>>,
32{
33    let (items, pages, current, total) = with_retry(retry, true, fetch).await.map_err(map_err)?;
34    Ok(Page {
35        items,
36        page: current,
37        pages,
38        total,
39    })
40}
41
42pub(crate) async fn collect_all<T, E, F, Fut, M>(
43    retry: &RetryPolicy,
44    map_err: M,
45    mut fetch: F,
46) -> Result<Vec<T>, ManagerError>
47where
48    M: Fn(E) -> ManagerError,
49    F: FnMut(i32) -> Fut,
50    Fut: std::future::Future<Output = Result<(Vec<T>, i32, i32), E>>,
51{
52    let mut out = Vec::new();
53    let mut page = 1;
54    loop {
55        let (items, pages, _current) = with_retry(retry, true, || fetch(page))
56            .await
57            .map_err(&map_err)?;
58        let empty = items.is_empty();
59        out.extend(items);
60        // Advance by a local monotonic counter rather than the server-echoed `current`: a server
61        // that misreports its cursor (e.g. always returns current=1) can't then spin us in an
62        // infinite, memory-exhausting loop. Stop on an empty page or once we reach the page count.
63        if empty || page >= pages {
64            return Ok(out);
65        }
66        page += 1;
67    }
68}