Skip to main content

acme_proxy_admin/admin/
subject.rs

1//! What a lookup was for, so "not found" reads the same on every surface.
2//!
3//! The CLI, the `/api` handlers and the `/ui` pages each refuse an unknown id
4//! with their own error type — exit code 3, a `404` JSON body, a `404` page —
5//! and used to spell the sentence each time, seventeen times over. They had
6//! already drifted: an unknown operator was "no such admin user" in a terminal
7//! and "no such operator" in a browser. The sentence lives here; each surface
8//! keeps only its wrapper.
9
10use std::fmt::Display;
11
12/// The kinds of row an operator names by id.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum Subject {
15    Account,
16    Order,
17    Job,
18    EabCredential,
19    Operator,
20    Session,
21    /// A profile, named rather than numbered.
22    Profile,
23}
24
25impl Subject {
26    /// The refusal for `id`, which names no row of this kind.
27    #[must_use]
28    pub fn missing(self, id: impl Display) -> String {
29        let noun = match self {
30            Self::Account => "account",
31            Self::Order => "order",
32            Self::Job => "job",
33            Self::EabCredential => "EAB credential",
34            Self::Operator => "operator",
35            Self::Session => "session",
36            Self::Profile => return format!("no profile named `{id}` is mounted"),
37        };
38        format!("no such {noun}: {id}")
39    }
40}
41
42#[cfg(test)]
43mod tests {
44    use super::*;
45
46    #[test]
47    fn each_subject_names_itself_and_the_id() {
48        assert_eq!(Subject::Account.missing("a-1"), "no such account: a-1");
49        assert_eq!(
50            Subject::EabCredential.missing("kid-9"),
51            "no such EAB credential: kid-9"
52        );
53        assert_eq!(
54            Subject::Profile.missing("le"),
55            "no profile named `le` is mounted"
56        );
57    }
58}