Skip to main content

energy_api/
error.rs

1//! Unified error type for the `energy-api` crate.
2
3use thiserror::Error;
4
5/// All errors that can be returned by the energy-api crate.
6#[derive(Debug, Error)]
7pub enum Error {
8    /// The remote service responded with a non-success HTTP status.
9    #[error("HTTP {status}: {body}")]
10    Http {
11        /// HTTP status code (e.g. `400`, `503`).
12        status: u16,
13        /// Response body text.
14        body: String,
15    },
16
17    /// The server returned a 307 Temporary Redirect — the caller should
18    /// follow the given URL and retry the request there.
19    #[error("Redirect to: {url}")]
20    Redirect {
21        /// Redirect target URL.
22        url: String,
23    },
24
25    /// A requested directory record does not exist.
26    #[error("record not found")]
27    NotFound,
28
29    /// JSON serialization or deserialization failure.
30    #[error("JSON error: {0}")]
31    Json(#[from] serde_json::Error),
32
33    /// An invalid URL was supplied or returned by the server.
34    #[error("URL error: {0}")]
35    Url(#[from] url::ParseError),
36
37    /// JWS signature creation or verification failed.
38    #[error("signature error: {0}")]
39    Signature(String),
40
41    /// Transport-level failure (TLS, DNS, connection reset, …).
42    #[error("transport error: {0}")]
43    Transport(String),
44
45    /// The remote endpoint violated the API protocol.
46    #[error("protocol error: {0}")]
47    Protocol(String),
48
49    /// A BDEW identifier failed validation (length, alphabet or check digit).
50    ///
51    /// Surfaced as HTTP 400: the caller supplied a malformed MaLo-, MeLo-,
52    /// NeLo-, SR- or TR-ID, and accepting it would let a bad identifier enter
53    /// the identification path.
54    #[error("invalid identifier: {0}")]
55    InvalidIdentifier(String),
56}
57
58#[cfg(feature = "client")]
59impl From<reqwest::Error> for Error {
60    fn from(e: reqwest::Error) -> Self {
61        if let Some(status) = e.status() {
62            Error::Http {
63                status: status.as_u16(),
64                body: e.to_string(),
65            }
66        } else {
67            Error::Transport(e.to_string())
68        }
69    }
70}
71
72#[cfg(feature = "websocket")]
73impl From<tokio_tungstenite::tungstenite::Error> for Error {
74    fn from(e: tokio_tungstenite::tungstenite::Error) -> Self {
75        Error::Transport(e.to_string())
76    }
77}