Skip to main content

liminal_server/health/endpoint/
access.rs

1//! ACCESS-011 R3: `/health` says how the exchange admits and decides.
2
3use crate::config::ServerConfig;
4use crate::config::types::AccessMode;
5
6/// `access: {mode, reach?, decisions?}` beside `lys_root`: the mode every
7/// exchange has, the reach of an open one, the decisions word of a Lys one.
8#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
9pub struct AccessHealth {
10    /// `open` or `gated`.
11    pub mode: &'static str,
12    /// `loopback` or `network`; only in open mode.
13    #[serde(skip_serializing_if = "Option::is_none")]
14    pub reach: Option<&'static str>,
15    /// `enforce` or `advise`; only with `[auth.lys]`.
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub decisions: Option<&'static str>,
18}
19
20impl From<&ServerConfig> for AccessHealth {
21    fn from(config: &ServerConfig) -> Self {
22        let mode = config.access_mode();
23        Self {
24            mode: match mode {
25                AccessMode::Open => "open",
26                AccessMode::Gated => "gated",
27            },
28            reach: (mode == AccessMode::Open).then(|| config.open_reach().word()),
29            decisions: config
30                .auth
31                .as_ref()
32                .and_then(|auth| auth.lys.as_ref())
33                .map(|lys| lys.decisions.word()),
34        }
35    }
36}
37
38impl AccessHealth {
39    /// A gated exchange on its bearer alone: what a health test that is not
40    /// about access models.
41    pub const BEARER: Self = Self {
42        mode: "gated",
43        reach: None,
44        decisions: None,
45    };
46
47    /// The same answer as one line, for `liminal check`:
48    /// `mode open, reach network` or `mode gated, decisions advise`.
49    #[must_use]
50    pub fn words(&self) -> String {
51        let mut words = vec![format!("mode {}", self.mode)];
52        words.extend(self.reach.map(|reach| format!("reach {reach}")));
53        words.extend(
54            self.decisions
55                .map(|decisions| format!("decisions {decisions}")),
56        );
57        words.join(", ")
58    }
59}