Skip to main content

lager/
error.rs

1//! Crate-wide error type.
2
3use std::fmt;
4
5/// Convenience alias used by every fallible API in this crate.
6pub type Result<T> = std::result::Result<T, Error>;
7
8/// All the ways a Lager box interaction can fail.
9#[derive(Debug)]
10#[non_exhaustive]
11pub enum Error {
12    /// The box could not be reached at all (DNS, TCP connect, transport
13    /// failure). Check network/Tailscale and that the box is online.
14    Connection(String),
15    /// The HTTP request timed out client-side. For long-running actions
16    /// (energy integration windows, `wait_for_level`) the crate widens the
17    /// timeout automatically, so hitting this usually means the box stalled.
18    Timeout(String),
19    /// The box accepted the request but reported failure. Carries the HTTP
20    /// status and the box's `error` message. Note the box can report failure
21    /// with HTTP 200 (e.g. a cross-role instrument conflict); this variant
22    /// covers that too.
23    Box {
24        /// HTTP status code of the response (200 is possible: the box
25        /// reports some conflicts as `success: false` with HTTP 200).
26        status: u16,
27        /// Human-readable error message from the box.
28        message: String,
29    },
30    /// HTTP 501: this box image does not serve the endpoint. The box needs a
31    /// software update.
32    UnsupportedByBox {
33        /// Human-readable error message from the box.
34        message: String,
35    },
36    /// The box's response could not be parsed into the expected shape.
37    Decode(String),
38    /// The net type exists in the Lager Python API but its box endpoint is
39    /// not yet available on the `:9000` HTTP API, so this crate ships it as a
40    /// documented stub. See `MISSING_ENDPOINTS.md` in the crate repository.
41    NotSupportedByBox {
42        /// Which net/feature was invoked (e.g. `"debug"`).
43        feature: &'static str,
44        /// What the box side would need to expose for this to work.
45        details: &'static str,
46    },
47    /// The box sits behind an authenticating gateway and the request was
48    /// denied (HTTP 401 with the `X-Gateway-Auth-Url` discovery header),
49    /// with no usable credential available. Sign in with the Lager CLI
50    /// (`lager login <auth_url>`) — this crate reuses the CLI's session —
51    /// or supply a token via `LagerBoxBuilder::bearer_token` /
52    /// `LAGER_GATEWAY_TOKEN`.
53    AuthRequired {
54        /// Hostname of the gated box.
55        box_host: String,
56        /// The auth server URL announced by the gateway.
57        auth_url: String,
58        /// What happened (no credential vs. rejected credential).
59        message: String,
60    },
61    /// Client-side configuration problem (bad host string, missing env var).
62    Config(String),
63    /// A streaming session (UART) reported an error, e.g. the net is in use
64    /// by another session or the device disappeared.
65    Stream(String),
66}
67
68impl fmt::Display for Error {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        match self {
71            Error::Connection(msg) => write!(
72                f,
73                "cannot reach the Lager box: {msg}. Check network/Tailscale and that the box is online and updated"
74            ),
75            Error::Timeout(msg) => write!(f, "request to the Lager box timed out: {msg}"),
76            Error::Box { status, message } => {
77                write!(f, "box error (HTTP {status}): {message}")
78            }
79            Error::UnsupportedByBox { message } => write!(
80                f,
81                "{message}. This box image does not support this endpoint; update the box"
82            ),
83            Error::Decode(msg) => write!(f, "could not decode box response: {msg}"),
84            Error::NotSupportedByBox { feature, details } => write!(
85                f,
86                "'{feature}' is not yet available over the box HTTP API: {details}"
87            ),
88            Error::AuthRequired { auth_url, message, .. } => write!(
89                f,
90                "{message}. Sign in with `lager login {auth_url}` (this crate reuses the \
91                 CLI's session), or set LAGER_GATEWAY_TOKEN / use \
92                 LagerBoxBuilder::bearer_token"
93            ),
94            Error::Config(msg) => write!(f, "configuration error: {msg}"),
95            Error::Stream(msg) => write!(f, "streaming session error: {msg}"),
96        }
97    }
98}
99
100impl std::error::Error for Error {}
101
102impl From<serde_json::Error> for Error {
103    fn from(e: serde_json::Error) -> Self {
104        Error::Decode(e.to_string())
105    }
106}