sl_glw/error.rs
1//! Error types for the `sl-glw` crate
2//!
3//! Three layered error enums plus an umbrella [`Error`] that wraps all of
4//! them. Layout mirrors `sl_map_apis::region` so the crates compose well.
5
6/// Errors that can occur while fetching a GLW event over HTTP and
7/// decoding its JSON body.
8#[expect(
9 clippy::module_name_repetitions,
10 reason = "the error types are the primary public surface of this module"
11)]
12#[derive(Debug, thiserror::Error)]
13pub enum FetchError {
14 /// reqwest-level transport error
15 #[error("HTTP error: {0}")]
16 Http(#[from] reqwest::Error),
17 /// the cache layer was unable to clone the request for revalidation
18 #[error("failed to clone request for creation of cache policy")]
19 FailedToCloneRequest,
20 /// the GLW server returned a non-success, non-404 status
21 #[error("HTTP {status} from GLW server at {url}: {body}")]
22 BadStatus {
23 /// HTTP status code returned by the server
24 status: reqwest::StatusCode,
25 /// the URL that produced the error
26 url: String,
27 /// the response body (truncated by the caller if needed)
28 body: String,
29 },
30 /// the response body exceeded the maximum we are willing to buffer
31 #[error("GLW response body exceeded the {limit}-byte limit")]
32 ResponseTooLarge {
33 /// the byte limit that was exceeded
34 limit: usize,
35 },
36 /// the configured base URL was not parseable as a [`url::Url`]
37 #[error("invalid GLW base URL: {0}")]
38 InvalidBaseUrl(#[from] url::ParseError),
39 /// the response body could not be parsed as JSON
40 #[error("JSON decode error: {0}")]
41 Json(#[from] serde_json::Error),
42 /// the parsed JSON did not satisfy the GLW schema
43 #[error("invalid event data: {0}")]
44 Parse(#[from] ParseError),
45}
46
47/// Errors that arise from validating GLW JSON values against their
48/// documented domains (e.g. wind direction must be 0..=359).
49#[expect(
50 clippy::module_name_repetitions,
51 reason = "the error types are the primary public surface of this module"
52)]
53#[derive(Debug, thiserror::Error)]
54pub enum ParseError {
55 /// a numeric field fell outside its allowed range
56 #[error("field {field} out of range: got {value}, expected {allowed}")]
57 OutOfRange {
58 /// the JSON path of the offending field (e.g. `"base.wind.dir"`)
59 field: &'static str,
60 /// the value rendered as a string (works for ints and floats)
61 value: String,
62 /// human-readable description of the allowed domain
63 allowed: &'static str,
64 },
65}
66
67/// Errors that arise from the three-tier cache around GLW events.
68#[expect(
69 clippy::module_name_repetitions,
70 reason = "the error types are the primary public surface of this module"
71)]
72#[derive(Debug, thiserror::Error)]
73pub enum GlwEventCacheError {
74 /// error decoding the JSON serialised cache policy
75 #[error("error decoding the JSON serialized CachePolicy: {0}")]
76 CachePolicyJsonDecodeError(#[from] serde_json::Error),
77 /// the underlying redb database returned an error opening the file
78 #[error("redb database error: {0}")]
79 DatabaseError(#[from] redb::DatabaseError),
80 /// the underlying redb transaction returned an error
81 #[error("redb transaction error: {0}")]
82 TransactionError(#[from] redb::TransactionError),
83 /// the underlying redb table returned an error
84 #[error("redb table error: {0}")]
85 TableError(#[from] redb::TableError),
86 /// the underlying redb storage returned an error
87 #[error("redb storage error: {0}")]
88 StorageError(#[from] redb::StorageError),
89 /// the underlying redb commit returned an error
90 #[error("redb commit error: {0}")]
91 CommitError(#[from] redb::CommitError),
92 /// the upstream HTTP fetch failed
93 #[error("error looking up GLW event via HTTP: {0}")]
94 FetchError(#[from] FetchError),
95 /// system time error while computing cache freshness
96 #[error("error handling system time for cache age calculations: {0}")]
97 SystemTimeError(#[from] std::time::SystemTimeError),
98}
99
100/// Umbrella error type that wraps every error this crate can produce.
101///
102/// Useful when a caller needs a single `Result` type across fetch, parse
103/// and cache boundaries. Rendering is infallible, so it has no variant
104/// here.
105#[derive(Debug, thiserror::Error)]
106pub enum Error {
107 /// an HTTP fetch or response-decoding error
108 #[error(transparent)]
109 Fetch(#[from] FetchError),
110 /// a JSON validation error
111 #[error(transparent)]
112 Parse(#[from] ParseError),
113 /// a cache-layer error
114 #[error(transparent)]
115 Cache(#[from] GlwEventCacheError),
116}