Skip to main content

gotify/
error.rs

1//! [`GotifyError`] and the crate's [`Result`] alias.
2
3use std::time::Duration;
4
5use thiserror::Error;
6
7/// Result alias for this crate's fallible operations.
8pub type Result<T> = std::result::Result<T, GotifyError>;
9
10/// Everything that can go wrong talking to a Gotify server.
11///
12/// Marked `#[non_exhaustive]`: this crate is meant to be published and
13/// consumed externally, so a caller that matches on this must include a
14/// wildcard arm — adding a new variant must never be a semver break for
15/// downstream code.
16#[derive(Debug, Error)]
17#[non_exhaustive]
18pub enum GotifyError {
19    /// [`crate::GotifyConfig::url`] was empty at [`crate::GotifyClient::new`].
20    #[error("GOTIFY_URL is not configured")]
21    MissingUrl,
22
23    /// The requested operation needs a client token
24    /// ([`crate::GotifyConfig::client_token`]), but none is configured.
25    /// Client tokens authenticate management operations (messages,
26    /// applications, clients, current user) — create one under **Clients**
27    /// in the Gotify web UI.
28    #[error(
29        "GOTIFY_CLIENT_TOKEN is required for this operation \
30         (create one under Clients in the Gotify web UI)"
31    )]
32    MissingClientToken,
33
34    /// [`crate::GotifyClient::send_message`] needs an app token
35    /// ([`crate::GotifyConfig::app_token`]), but none is configured. App
36    /// tokens are distinct from client tokens — create one under
37    /// **Applications** in the Gotify web UI.
38    #[error(
39        "GOTIFY_APP_TOKEN is required to send a message \
40         (create one under Applications in the Gotify web UI)"
41    )]
42    MissingAppToken,
43
44    /// The underlying `reqwest::Client` failed to construct.
45    #[error("failed to build the HTTP client: {0}")]
46    ClientBuild(#[source] reqwest::Error),
47
48    /// HTTP 401 — the configured token was rejected.
49    #[error("Gotify rejected the request as unauthorized (HTTP 401): {0}")]
50    Unauthorized(String),
51
52    /// HTTP 404.
53    #[error("Gotify endpoint not found for {method} {url} (HTTP 404)")]
54    NotFound {
55        /// HTTP method of the request.
56        method: String,
57        /// Full request URL.
58        url: String,
59    },
60
61    /// HTTP 429.
62    #[error(
63        "Gotify rate limit exceeded for {method} {url} (HTTP 429){}",
64        retry_after.map(|d| format!(" - retry after {}s", d.as_secs())).unwrap_or_default()
65    )]
66    RateLimited {
67        /// HTTP method of the rate-limited request.
68        method: String,
69        /// Full request URL.
70        url: String,
71        /// Parsed `Retry-After` response header, when present and expressed
72        /// in seconds (the HTTP-date form is not parsed).
73        retry_after: Option<Duration>,
74    },
75
76    /// Any other non-success status. `body` is JSON when the response was
77    /// JSON, otherwise the raw text; boxed to keep this enum small.
78    #[error("Gotify HTTP {status} for {url}: {body}")]
79    UnexpectedStatus {
80        /// HTTP status code.
81        status: u16,
82        /// Full request URL.
83        url: String,
84        /// Best-effort response body.
85        body: Box<serde_json::Value>,
86    },
87
88    /// The response body wasn't valid JSON on an otherwise-successful status.
89    #[error("failed to decode Gotify response from {url}: {source}")]
90    Decode {
91        /// Full request URL.
92        url: String,
93        /// The underlying deserialization failure.
94        #[source]
95        source: serde_json::Error,
96    },
97
98    /// Request timed out.
99    #[error("Gotify request timed out: {method} {url}")]
100    Timeout {
101        /// HTTP method of the request.
102        method: String,
103        /// Full request URL.
104        url: String,
105        /// The underlying transport error.
106        #[source]
107        source: reqwest::Error,
108    },
109
110    /// Failed to connect to the configured URL.
111    #[error("failed to connect to Gotify at {url} ({method}) — check GOTIFY_URL is reachable")]
112    Connect {
113        /// HTTP method of the request.
114        method: String,
115        /// Full request URL.
116        url: String,
117        /// The underlying transport error.
118        #[source]
119        source: reqwest::Error,
120    },
121
122    /// Any other transport-level failure.
123    #[error("Gotify request failed: {method} {url}: {source}")]
124    Request {
125        /// HTTP method of the request.
126        method: String,
127        /// Full request URL.
128        url: String,
129        /// The underlying transport error.
130        #[source]
131        source: reqwest::Error,
132    },
133}