gitlab/api/paged/
pagination.rs

1// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
2// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
3// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
4// option. This file may not be copied, modified, or distributed
5// except according to those terms.
6
7use thiserror::Error;
8
9use crate::api::paged::LinkHeaderParseError;
10
11/// Errors which may occur with pagination.
12#[derive(Debug, Error)]
13#[non_exhaustive]
14pub enum PaginationError {
15    /// A `Link` HTTP header can fail to parse.
16    #[error("failed to parse a Link HTTP header: {}", source)]
17    LinkHeader {
18        /// The source of the error.
19        #[from]
20        source: LinkHeaderParseError,
21    },
22    /// An invalid URL can be returned.
23    #[error("failed to parse a Link HTTP header URL: {}", source)]
24    InvalidUrl {
25        /// The source of the error.
26        #[from]
27        source: url::ParseError,
28    },
29}
30
31/// Pagination options for GitLab.
32#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
33#[non_exhaustive]
34pub enum Pagination {
35    /// Return all results.
36    ///
37    /// Note that some endpoints may have a server-side limit to the number of results (e.g.,
38    /// `/projects` is limited to 10000 results).
39    #[default]
40    All,
41    /// Return all results.
42    ///
43    /// Some endpoints return a 500 Internal Server Error when trying to fetch 100 results at once.
44    /// Limit to a number of results per page.
45    AllPerPageLimit(usize),
46    /// Limit to a number of results.
47    Limit(usize),
48}
49
50const MAX_PAGE_SIZE: usize = 100;
51
52impl Pagination {
53    pub(crate) fn page_limit(self) -> usize {
54        match self {
55            Pagination::All => MAX_PAGE_SIZE,
56            Pagination::AllPerPageLimit(size) => size,
57            Pagination::Limit(size) => size.min(MAX_PAGE_SIZE),
58        }
59    }
60
61    pub(crate) fn is_last_page(self, last_page_size: usize, num_results: usize) -> bool {
62        // If the last page has fewer elements than our limit, we're definitely done.
63        if last_page_size < self.page_limit() {
64            return true;
65        }
66
67        // Otherwise, check if we have results which fill our limit.
68        if let Pagination::Limit(limit) = self {
69            return limit <= num_results;
70        }
71
72        // We're not done yet.
73        false
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use crate::api::Pagination;
80
81    #[test]
82    fn pagination_default() {
83        assert_eq!(Pagination::default(), Pagination::All);
84    }
85
86    #[test]
87    fn test_pagination_page_limit() {
88        assert_eq!(Pagination::All.page_limit(), super::MAX_PAGE_SIZE);
89        assert_eq!(
90            Pagination::Limit(super::MAX_PAGE_SIZE).page_limit(),
91            super::MAX_PAGE_SIZE,
92        );
93        assert_eq!(Pagination::Limit(1).page_limit(), 1);
94        assert_eq!(Pagination::AllPerPageLimit(42).page_limit(), 42);
95    }
96}