Skip to main content

acme_proxy/cli/
window.rs

1//! The `--limit`/`--offset` window every paged listing takes.
2//!
3//! Every listing in the binary pages — `account`, `order` (both queries),
4//! `audit`, `jobs`, `eab`, `upstream order`, `admin user` and `admin session` —
5//! and every one of them wants the same default, the same two clamps and the
6//! same envelope under `--json`. That envelope is
7//! [`crate::cli::render::json_page`]; this is the window that produced it.
8
9/// Default rows per page.
10///
11/// There is deliberately no "everything" spelling, and `--limit 0` is not a way
12/// around it: `orders` and `audit_log` each grow a row per issuance for the
13/// life of the deployment, so on a year-old CA an unwindowed listing is a
14/// terminal full of scrollback and a table loaded into memory. Page with
15/// `--offset`; the `N of M row(s)` footer is what says there is more.
16pub const DEFAULT_LIMIT: i64 = 50;
17
18/// A resolved window, clamped into something a query can be handed.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub struct Window {
21    pub limit: i64,
22    pub offset: i64,
23}
24
25impl Window {
26    /// Clamps a caller's window rather than refusing it.
27    ///
28    /// A `--limit 0` or a negative offset is nonsense rather than an attack, and
29    /// answering with the smallest usable page is more useful than an error;
30    /// passed through to SQL, `LIMIT -1` means *no limit* in SQLite, which is
31    /// the one answer a window must never accidentally give.
32    ///
33    /// Deliberately **not** clamped to `admin.page_size_max`: that key is a
34    /// ceiling on what an HTTP caller may ask the server for, and this front end
35    /// answers to a shell on the host. There is no upper bound on the offset
36    /// either, unlike `webadmin::handlers::paging`, because nothing here
37    /// computes `offset + limit` — a terminal has no "next page" link to place.
38    #[must_use]
39    pub fn resolve(limit: i64, offset: i64) -> Self {
40        Self {
41            limit: limit.max(1),
42            offset: offset.max(0),
43        }
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50
51    #[test]
52    fn a_nonsense_window_is_clamped_rather_than_refused() {
53        for (limit, expected) in [(50, 50), (1, 1), (0, 1), (-5, 1)] {
54            assert_eq!(Window::resolve(limit, 0).limit, expected, "limit={limit}");
55        }
56        for (offset, expected) in [(0, 0), (7, 7), (-7, 0)] {
57            assert_eq!(
58                Window::resolve(50, offset).offset,
59                expected,
60                "offset={offset}"
61            );
62        }
63    }
64}