use http::StatusCode;
use serde::Deserialize;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("HTTP transport error: {0}")]
Http(#[from] reqwest::Error),
#[error("IG API error ({status}): {0}", .source.error_code)]
Api {
status: StatusCode,
#[source]
source: ApiError,
},
#[error("authentication error: {0}")]
Auth(String),
#[error("rate limited by IG ({0})")]
RateLimited(String),
#[error("failed to deserialise response: {0}")]
Deserialization(#[from] serde_json::Error),
#[error("invalid configuration: {0}")]
Config(String),
#[error("invalid input: {0}")]
InvalidInput(String),
#[error("URL error: {0}")]
Url(#[from] url::ParseError),
#[error("invalid HTTP header value: {0}")]
HeaderValue(#[from] http::header::InvalidHeaderValue),
}
#[derive(Debug, Clone, Deserialize, thiserror::Error)]
#[error("{error_code}")]
pub struct ApiError {
#[serde(rename = "errorCode")]
pub error_code: String,
#[serde(flatten, default)]
pub extra: serde_json::Map<String, serde_json::Value>,
}
impl Error {
pub fn is_auth(&self) -> bool {
match self {
Error::Auth(_) => true,
Error::Api { source, .. } => {
let c = source.error_code.as_str();
c.contains("oauth-token-invalid")
|| c.contains("client-token-missing")
|| c.contains("security-token")
}
_ => false,
}
}
pub fn is_rate_limited(&self) -> bool {
match self {
Error::RateLimited(_) => true,
Error::Api { source, .. } => source.error_code.contains("api-rate-exceeded"),
_ => false,
}
}
}