Skip to main content

esi_openapi/
errors.rs

1//! Errors
2
3use http::header::ToStrError;
4use std::num::ParseIntError;
5use thiserror::Error;
6
7/// Errors that can occur when dealing with ESI.
8#[derive(Debug, Error)]
9#[non_exhaustive]
10pub enum EsiError {
11    /// Error that can be thrown if the `EsiBuilder` struct is
12    /// invalid when `.build()` is called.
13    #[error("Missing required builder struct value '{0}'")]
14    EmptyClientValue(String),
15    /// Error that can be thrown if the `EsiBuilder` struct is
16    /// invalid when `.build()` is called.
17    /// You need to specify either a client secret or enable application auth
18    #[error("Authentication flow information missing. You need to either specify client_secret or enable application auth.")]
19    MissingAuthenticationFlowInformation,
20    /// You have to retrieve the ESI spec via `Esi::update_spec`
21    /// before making this call.
22    #[error("Missing spec")]
23    EmptySpec,
24    /// Error that could be thrown if the access token JWT from SSO
25    /// is invalid, whether due to tampering or some other reason.
26    #[error("Invalid JWT: {0}")]
27    InvalidJWT(String),
28    /// Validation of the JWT failed.
29    #[cfg(feature = "validate_jwt")]
30    #[error("JWT validation failed")]
31    JwtValidationFailed(#[from] jsonwebtoken::errors::Error),
32    /// Error that can be thrown by any function that makes HTTP
33    /// calls our to external resources for response codes that
34    /// aren't valid as defined [by reqwest].
35    /// [by reqwest]: https://docs.rs/reqwest/0.10.6/reqwest/struct.StatusCode.html#method.is_success
36    #[error("Invalid HTTP status code received: {0}")]
37    InvalidStatusCode(u16),
38    /// The resource was deleted. ESI keeps answering for it as a tombstone for
39    /// `tombstone_ttl_secs` seconds (`x-tombstone-ttl`), so this is not a plain
40    /// "not found". Only operations that declare a tombstone TTL return it, for
41    /// HTTP `404` and `410`.
42    #[error("The resource is gone (HTTP {status}); ESI keeps it as a tombstone for {tombstone_ttl_secs}s")]
43    Gone {
44        /// The HTTP status code of the response.
45        status: u16,
46        /// How long, in seconds, ESI keeps the tombstone.
47        tombstone_ttl_secs: i64,
48    },
49    /// Error for if the provided user-agent header value has invalid characters.
50    #[error("Invalid HTTP header value")]
51    InvalidUserAgentHeader(#[from] http::header::InvalidHeaderValue),
52    /// Error for if the underlying `reqwest::Client` could not be constructed.
53    #[error("HTTP client error: {0}")]
54    ReqwestError(#[from] reqwest::Error),
55    /// Error for if the String cannot be converted into a valid HTTP method.
56    #[error("Invalid HTTP method")]
57    HttpMethodError(#[from] http::method::InvalidMethod),
58    /// Error for if a request is made to an endpoint that requires authentication,
59    /// but no access token is present in the Esi struct.
60    #[error("This endpoint requires an access token")]
61    MissingAuthentication,
62    /// Error for not finding the passed operationId in the ESI OpenAPI spec.
63    #[error("Could not resolve operationId '{0}' to a URL path")]
64    UnknownOperationID(String),
65    /// Error for being unable to parse the OpenAPI spec from ESI.
66    #[error("Error occurred while parsing the OpenAPI spec at: {0}")]
67    FailedSpecParse(String),
68    /// Error for being unable to parse JSON from anywhere.
69    #[error("Failed to serialize/deserialize JSON; this may be due to unexpected data or invalid struct field(s)")]
70    FailedJsonParse(#[from] serde_json::Error),
71    /// Error for being unable to get the current timestamp.
72    #[error("Could not get current timestamp: {0}")]
73    Timestamp(#[from] std::time::SystemTimeError),
74    /// Error for being unable to read response header.
75    #[error("Could not read response header value: {0}")]
76    HeaderReadError(#[from] ToStrError),
77    /// Error for being unable to parse header value.
78    #[error("Could not parse response header - {0}: {1}")]
79    HeaderParseError(String, ParseIntError),
80    /// Error for enforcing ESI error limit
81    #[error("Refusing to process request as we are error limited for {0}ms")]
82    ErrorLimited(i64),
83    /// ESI answered `429 Too Many Requests`: the rate-limit bucket for this
84    /// route group is empty. Wait `retry_after_secs` (from the `Retry-After`
85    /// header, when present) before calling routes in `group` again.
86    #[error("Rate limited by ESI (group: {group:?}, retry after: {retry_after_secs:?}s)")]
87    RateLimited {
88        /// Rate-limit group from the `X-Ratelimit-Group` header, if present.
89        group: Option<String>,
90        /// Seconds to wait, from the `Retry-After` header, if present.
91        retry_after_secs: Option<u64>,
92    },
93    /// Error for the access token being used after expiring (and therefore
94    /// being unable to be used for ESI) and no refresh token being present
95    /// to fetch another access token.
96    #[error("Access token is expired, and no refresh token is present")]
97    AccessTokenExpired,
98    /// Error when a access_token needs to be refreshed, but no refresh
99    /// token could be found to refresh the access token
100    #[error("No refresh token available to request an access token")]
101    NoRefreshTokenAvailable,
102}
103
104/// Crate `Result` wrapper.
105pub type EsiResult<T> = Result<T, EsiError>;