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