Skip to main content

ghl_sdk/
error.rs

1use std::time::Duration;
2
3use reqwest::StatusCode;
4
5/// All errors returned by this crate.
6#[derive(Debug, thiserror::Error)]
7#[non_exhaustive]
8pub enum Error {
9    /// The API returned a non-success status code.
10    #[error("GoHighLevel API error ({status}): {message}")]
11    Api {
12        /// HTTP status returned by the API.
13        status: StatusCode,
14        /// Human-readable message extracted from the error body.
15        message: String,
16        /// Request id echoed by the API, when present (useful in support threads).
17        request_id: Option<String>,
18    },
19
20    /// The API returned 429 and retries were exhausted.
21    #[error("rate limited by GoHighLevel API{}", retry_after_hint(.retry_after))]
22    RateLimited {
23        /// Server-suggested wait before retrying, from the `Retry-After` header.
24        retry_after: Option<Duration>,
25    },
26
27    /// Authentication problems: missing credentials, failed token refresh, etc.
28    #[error("authentication error: {0}")]
29    Auth(String),
30
31    /// Network / TLS / protocol-level failures.
32    #[error("transport error: {0}")]
33    Transport(#[from] reqwest::Error),
34
35    /// The response body could not be decoded into the expected type.
36    #[error("failed to decode response from `{endpoint}`: {source}")]
37    Decode {
38        /// The API path whose response failed to decode.
39        endpoint: String,
40        /// The underlying deserialization error.
41        #[source]
42        source: serde_json::Error,
43    },
44
45    /// Invalid client configuration.
46    #[error("configuration error: {0}")]
47    Config(String),
48}
49
50fn retry_after_hint(retry_after: &Option<Duration>) -> String {
51    match retry_after {
52        Some(d) => format!(" (retry after {}s)", d.as_secs()),
53        None => String::new(),
54    }
55}
56
57/// Crate-wide result alias.
58pub type Result<T, E = Error> = std::result::Result<T, E>;