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}