use std::collections::BTreeMap;
use bytes::Bytes;
use http::StatusCode;
use snafu::Snafu;
#[derive(Debug, Snafu)]
pub enum ApiError {
#[snafu(display("The API returned an HTTP 401 UNAUTHORIZED status"))]
Unauthorized {
headers: BTreeMap<String, String>,
body: Bytes,
},
#[snafu(display("The API returned an HTTP 403 FORBIDDEN status"))]
Forbidden {
headers: BTreeMap<String, String>,
body: Bytes,
},
#[snafu(display("The API returned an HTTP 404 NOT FOUND status"))]
NotFound {
headers: BTreeMap<String, String>,
body: Bytes,
},
#[snafu(display("The API returned an HTTP 500 INTERNAL SERVER ERROR status"))]
InternalServerError {
headers: BTreeMap<String, String>,
body: Bytes,
},
#[snafu(display("Received non-successful HTTP status code {code}"))]
NonSuccessfulStatusCode {
headers: BTreeMap<String, String>,
code: StatusCode,
body: Bytes,
},
}
impl ApiError {
#[allow(clippy::result_large_err)]
pub fn try_read_from_http_response(
http_response: http::Response<Bytes>,
) -> Result<Self, http::Response<Bytes>> {
let status = http_response.status();
let headers = http_response
.headers()
.into_iter()
.map(|(k, v)| {
(
k.to_string(),
String::from_utf8_lossy(v.as_bytes()).to_string(),
)
})
.collect();
match status {
StatusCode::UNAUTHORIZED => Ok(Self::Unauthorized {
headers,
body: http_response.into_body(),
}),
StatusCode::FORBIDDEN => Ok(Self::Forbidden {
headers,
body: http_response.into_body(),
}),
StatusCode::NOT_FOUND => Ok(Self::NotFound {
headers,
body: http_response.into_body(),
}),
StatusCode::INTERNAL_SERVER_ERROR => Ok(Self::InternalServerError {
headers,
body: http_response.into_body(),
}),
code if !code.is_success() => Ok(Self::NonSuccessfulStatusCode {
headers,
code,
body: http_response.into_body(),
}),
_ => Err(http_response),
}
}
pub const fn is_http_status_code(&self, expected_code: StatusCode) -> bool {
let c = expected_code.as_u16();
match self {
ApiError::Unauthorized { .. } => c == StatusCode::UNAUTHORIZED.as_u16(),
ApiError::Forbidden { .. } => c == StatusCode::FORBIDDEN.as_u16(),
ApiError::NotFound { .. } => c == StatusCode::NOT_FOUND.as_u16(),
ApiError::InternalServerError { .. } => c == StatusCode::INTERNAL_SERVER_ERROR.as_u16(),
ApiError::NonSuccessfulStatusCode { code, .. } => c == code.as_u16(),
}
}
pub const fn is_unauthorized(&self) -> bool {
self.is_http_status_code(StatusCode::UNAUTHORIZED)
}
pub const fn is_forbidden(&self) -> bool {
self.is_http_status_code(StatusCode::FORBIDDEN)
}
pub const fn is_not_found(&self) -> bool {
self.is_http_status_code(StatusCode::NOT_FOUND)
}
pub const fn is_internal_server_error(&self) -> bool {
self.is_http_status_code(StatusCode::INTERNAL_SERVER_ERROR)
}
}