use serde::Deserialize;
#[derive(Debug, Clone, Deserialize)]
pub struct ApiError {
pub code: String,
pub message: String,
}
#[derive(Debug, Deserialize)]
pub(crate) struct ErrorEnvelope {
pub error: ApiError,
}
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("authentication failed: {}", .0.message)]
Auth(ApiError),
#[error("insufficient scope: {}", .0.message)]
Scope(ApiError),
#[error("invalid request: {}", .0.message)]
Validation(ApiError),
#[error("not found: {}", .0.message)]
NotFound(ApiError),
#[error("rate limited: {}", .0.message)]
RateLimited(ApiError),
#[error("api error {status}: {}", .body.message)]
Api { status: u16, body: ApiError },
#[error("transport error: {0}")]
Transport(#[from] reqwest::Error),
#[error("unexpected response shape: {0}")]
Decode(String),
}
impl Error {
pub fn code(&self) -> Option<&str> {
match self {
Error::Auth(e)
| Error::Scope(e)
| Error::Validation(e)
| Error::NotFound(e)
| Error::RateLimited(e)
| Error::Api { body: e, .. } => Some(&e.code),
_ => None,
}
}
pub(crate) fn from_status(status: u16, body: ApiError) -> Self {
match status {
401 => Error::Auth(body),
403 => Error::Scope(body),
400 => Error::Validation(body),
404 => Error::NotFound(body),
429 => Error::RateLimited(body),
_ => Error::Api { status, body },
}
}
}
pub type Result<T> = std::result::Result<T, Error>;