Skip to main content

fmp_rust_sdk/
error.rs

1use std::time::Duration;
2
3/// A result from an FMP HTTP request or configuration operation.
4pub type Result<T> = std::result::Result<T, Error>;
5
6/// The request URL and query parameters. Authentication is sent in a header,
7/// never included in this value.
8#[derive(Clone, Debug, Eq, PartialEq)]
9pub struct RequestContext {
10    pub url: String,
11}
12
13impl std::fmt::Display for RequestContext {
14    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15        formatter.write_str(&self.url)
16    }
17}
18
19/// Classification of a provider error; the original status and body are retained.
20#[derive(Clone, Copy, Debug, Eq, PartialEq)]
21pub enum ApiErrorKind {
22    Authentication,
23    PlanRestricted,
24    RateLimited,
25    HttpStatus,
26    Provider,
27}
28
29/// A JSON/schema error whose diagnostic message has had credentials removed.
30/// The original serde error cannot be retained because it may quote a secret
31/// response value in its Display, Debug, or source chain.
32#[derive(Debug, thiserror::Error)]
33#[error("{message}")]
34pub struct JsonDecodeError {
35    pub message: String,
36    pub line: usize,
37    pub column: usize,
38}
39
40/// Specific, contextual failures. API keys are redacted from all retained bodies.
41#[derive(Debug, thiserror::Error)]
42pub enum Error {
43    #[error("invalid SDK configuration: {reason}")]
44    Configuration { reason: String },
45    #[error("cannot construct GET {request}: {source}")]
46    Request {
47        request: RequestContext,
48        #[source]
49        source: reqwest::Error,
50    },
51    #[error("GET {request} failed in transport: {source}")]
52    Transport {
53        request: RequestContext,
54        #[source]
55        source: reqwest::Error,
56    },
57    #[error("GET {request} returned HTTP {status}, but its body could not be read: {source}")]
58    ResponseBody {
59        request: RequestContext,
60        status: u16,
61        #[source]
62        source: reqwest::Error,
63    },
64    #[error("GET {request} failed ({kind:?}, HTTP {status}): {body}")]
65    Api {
66        request: RequestContext,
67        kind: ApiErrorKind,
68        status: u16,
69        body: String,
70        retry_after: Option<Duration>,
71    },
72    #[error("GET {request} returned invalid JSON/schema (HTTP {status}): {source}; body: {body}")]
73    Decode {
74        request: RequestContext,
75        status: u16,
76        body: String,
77        #[source]
78        source: JsonDecodeError,
79    },
80    #[error("GET {request} returned non-UTF-8 text (HTTP {status}): {source}")]
81    Encoding {
82        request: RequestContext,
83        status: u16,
84        #[source]
85        source: std::str::Utf8Error,
86    },
87    #[error(
88        "GET {request} returned an invalid {header} header (HTTP {status}): {reason}; body: {body}"
89    )]
90    ResponseHeader {
91        request: RequestContext,
92        status: u16,
93        header: &'static str,
94        reason: String,
95        body: String,
96    },
97}