Skip to main content

cognitum_one/
error.rs

1use std::time::Duration;
2
3use thiserror::Error;
4
5/// Errors returned by the Cognitum SDK.
6#[derive(Error, Debug)]
7pub enum Error {
8    /// Authentication failed (401).
9    #[error("authentication failed: {0}")]
10    Auth(String),
11
12    /// Rate limited (429). The caller should retry after the given duration.
13    ///
14    /// `retry_after_ms` is populated from the server's hint, resolved in
15    /// ADR-0005 §"429 handling" order:
16    /// 1. `Retry-After` header (seconds integer),
17    /// 2. `Retry-After` header (HTTP-date — delta from now),
18    /// 3. JSON body `retry_after_us` (microseconds, seed convention),
19    /// 4. JSON body `error` text matching `"retry after Ns"`,
20    /// 5. ADR-0005 equal-jitter fallback.
21    ///
22    /// Body signal wins over header when both are present (proxies strip
23    /// headers but preserve the body).
24    #[error("rate limited, retry after {retry_after_ms}ms")]
25    RateLimit {
26        /// Milliseconds to wait before retrying.
27        retry_after_ms: u64,
28    },
29
30    /// The request failed server-side validation.
31    #[error("validation error: {0}")]
32    Validation(String),
33
34    /// The requested resource was not found (404).
35    #[error("not found: {0}")]
36    NotFound(String),
37
38    /// A non-specific API error with an HTTP status code.
39    #[error("API error {code}: {message}")]
40    Api {
41        /// HTTP status code.
42        code: u16,
43        /// Human-readable error message from the server.
44        message: String,
45    },
46
47    /// An underlying HTTP transport error from reqwest.
48    #[error("HTTP error: {0}")]
49    Http(#[from] reqwest::Error),
50
51    /// JSON serialization / deserialization error.
52    #[error("JSON error: {0}")]
53    Json(#[from] serde_json::Error),
54}
55
56impl Error {
57    /// If this is a `RateLimit` error, return the retry delay as a
58    /// [`Duration`]. Ergonomic accessor so callers don't need to convert
59    /// `retry_after_ms` manually.
60    pub fn retry_after(&self) -> Option<Duration> {
61        match self {
62            Error::RateLimit { retry_after_ms } => Some(Duration::from_millis(*retry_after_ms)),
63            _ => None,
64        }
65    }
66}