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
47#[derive(Debug, Clone, Deserialize, thiserror::Error)]
52#[error("{error_code}")]
53pub struct ApiError {
54 #[serde(rename = "errorCode")]
55 pub error_code: String,
56 #[serde(flatten, default)]
57 pub extra: serde_json::Map<String, serde_json::Value>,
58}
59
60impl Error {
61 pub fn is_auth(&self) -> bool {
64 match self {
65 Error::Auth(_) => true,
66 Error::Api { source, .. } => {
67 let c = source.error_code.as_str();
68 c.contains("oauth-token-invalid")
69 || c.contains("client-token-missing")
70 || c.contains("security-token")
71 }
72 _ => false,
73 }
74 }
75
76 pub fn is_rate_limited(&self) -> bool {
78 match self {
79 Error::RateLimited(_) => true,
80 Error::Api { source, .. } => source.error_code.contains("api-rate-exceeded"),
81 _ => false,
82 }
83 }
84}