1use http::StatusCode;
8use serde::Deserialize;
9
10pub type Result<T> = std::result::Result<T, Error>;
12
13#[derive(Debug, thiserror::Error)]
14pub enum Error {
15 #[error("HTTP transport error: {0}")]
16 Http(#[from] reqwest::Error),
17
18 #[error("IG API error ({status}): {0}", .source.error_code)]
19 Api {
20 status: StatusCode,
21 #[source]
22 source: ApiError,
23 },
24
25 #[error("authentication error: {0}")]
26 Auth(String),
27
28 #[error("rate limited by IG ({0})")]
29 RateLimited(String),
30
31 #[error("failed to deserialise response: {0}")]
32 Deserialization(#[from] serde_json::Error),
33
34 #[error("invalid configuration: {0}")]
35 Config(String),
36
37 #[error("invalid input: {0}")]
38 InvalidInput(String),
39
40 #[error("URL error: {0}")]
41 Url(#[from] url::ParseError),
42
43 #[error("invalid HTTP header value: {0}")]
44 HeaderValue(#[from] http::header::InvalidHeaderValue),
45
46 #[error("streaming error: {0}")]
49 Streaming(String),
50}
51
52#[derive(Debug, Clone, Deserialize, thiserror::Error)]
57#[error("{error_code}")]
58pub struct ApiError {
59 #[serde(rename = "errorCode")]
60 pub error_code: String,
61 #[serde(flatten, default)]
62 pub extra: serde_json::Map<String, serde_json::Value>,
63}
64
65impl Error {
66 pub fn is_auth(&self) -> bool {
70 match self {
71 Error::Auth(_) => true,
72 Error::Api { source, .. } => {
73 let c = source.error_code.as_str();
74 c.contains("oauth-token-invalid")
75 || c.contains("client-token-invalid")
76 || c.contains("client-token-missing")
77 || c.contains("security-token")
78 }
79 _ => false,
80 }
81 }
82
83 pub fn is_rate_limited(&self) -> bool {
85 match self {
86 Error::RateLimited(_) => true,
87 Error::Api { source, .. } => source.error_code.contains("api-rate-exceeded"),
88 _ => false,
89 }
90 }
91}