1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
//! Synchronous implementation of automatic pagination requests.

use crate::{model::Page, ClientError, ClientResult};

/// Alias for `Iterator<Item = T>`, since sync mode is enabled.
pub type Paginator<'a, T> = Box<dyn Iterator<Item = T> + 'a>;

pub fn paginate_with_ctx<'a, Ctx: 'a, T: 'a, Request: 'a>(
    ctx: Ctx,
    req: Request,
    page_size: u32,
) -> Paginator<'a, ClientResult<T>>
where
    Request: Fn(&Ctx, u32, u32) -> ClientResult<Page<T>>,
{
    paginate(move |limit, offset| req(&ctx, limit, offset), page_size)
}

/// This is used to handle paginated requests automatically.
pub fn paginate<'a, T: 'a, Request: 'a>(
    req: Request,
    page_size: u32,
) -> Paginator<'a, ClientResult<T>>
where
    Request: Fn(u32, u32) -> ClientResult<Page<T>>,
{
    let pages = PageIterator {
        req,
        offset: 0,
        done: false,
        page_size,
    };

    Box::new(pages.flat_map(|result| ResultIter::new(result.map(|page| page.items.into_iter()))))
}

/// Iterator that repeatedly calls a function that returns a page until an empty
/// page is returned.
struct PageIterator<Request> {
    req: Request,
    offset: u32,
    done: bool,
    page_size: u32,
}

impl<T, Request> Iterator for PageIterator<Request>
where
    Request: Fn(u32, u32) -> ClientResult<Page<T>>,
{
    type Item = ClientResult<Page<T>>;

    fn next(&mut self) -> Option<Self::Item> {
        if self.done {
            return None;
        }

        match (self.req)(self.page_size, self.offset) {
            Ok(page) if page.items.is_empty() => {
                self.done = true;
                None
            }
            Ok(page) => {
                self.offset += page.items.len() as u32;
                Some(Ok(page))
            }
            Err(e) => Some(Err(e)),
        }
    }
}

/// Helper to transform a `Result<Iterator<Item = T>, E>` into an `Iterator<Item
/// = Result<T, E>>`.
struct ResultIter<T, I: Iterator<Item = T>> {
    inner: Option<I>,
    err: Option<ClientError>,
}

impl<T, I: Iterator<Item = T>> ResultIter<T, I> {
    pub fn new(res: ClientResult<I>) -> Self {
        match res {
            Ok(inner) => ResultIter {
                inner: Some(inner),
                err: None,
            },
            Err(err) => ResultIter {
                inner: None,
                err: Some(err),
            },
        }
    }
}

impl<T, I: Iterator<Item = T>> Iterator for ResultIter<T, I> {
    type Item = ClientResult<T>;

    fn next(&mut self) -> Option<Self::Item> {
        match (self.err.take(), &mut self.inner) {
            (Some(err), _) => Some(Err(err)),
            (None, Some(inner)) => inner.next().map(Ok),
            _ => None, // Error already taken
        }
    }
}