Skip to main content

alpr/
pagination.rs

1use std::collections::VecDeque;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::Error;
6
7/// One page of a list, and the cursor of the next one.
8#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
9pub struct Page<T> {
10    /// The page's items.
11    #[serde(rename = "data", default = "Vec::new")]
12    pub items: Vec<T>,
13    /// The cursor to ask the next page with; `None` on the last page.
14    #[serde(default)]
15    pub next: Option<String>,
16}
17
18/// Walks pages from the cursor `first` (the newest page when `None`) until
19/// the last one, yielding every item. A page is fetched only when the items
20/// before it were taken, a cursor that does not move ends the walk, and an
21/// error ends it too.
22pub(crate) fn iterate<'a, T: 'a>(
23    first: Option<String>,
24    mut fetch: impl FnMut(Option<&str>) -> Result<Page<T>, Error> + 'a,
25) -> impl Iterator<Item = Result<T, Error>> + 'a {
26    let mut cursor = first.filter(|cursor| !cursor.is_empty());
27    let mut items = VecDeque::new();
28    let mut done = false;
29    std::iter::from_fn(move || loop {
30        if let Some(item) = items.pop_front() {
31            return Some(Ok(item));
32        }
33        if done {
34            return None;
35        }
36        match fetch(cursor.as_deref()) {
37            Ok(result) => {
38                items.extend(result.items);
39                // A cursor that does not move would ask for the same page forever.
40                match result.next.filter(|next| !next.is_empty()) {
41                    Some(next) if cursor.as_deref() != Some(next.as_str()) => cursor = Some(next),
42                    _ => done = true,
43                }
44            }
45            Err(error) => {
46                done = true;
47                return Some(Err(error));
48            }
49        }
50    })
51}