use actix_web::{
ResponseError,
http::{
StatusCode,
header::{self, ContentType},
},
};
#[derive(Debug, PartialEq)]
pub struct ErrorWithStatus {
pub status: StatusCode,
}
impl std::fmt::Display for ErrorWithStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.status)
}
}
impl std::error::Error for ErrorWithStatus {}
impl ResponseError for ErrorWithStatus {
fn status_code(&self) -> StatusCode {
self.status
}
fn error_response(&self) -> actix_web::HttpResponse {
let mut resp_builder = actix_web::HttpResponse::build(self.status);
resp_builder.content_type(ContentType::plaintext());
if self.status == StatusCode::UNAUTHORIZED {
resp_builder.insert_header((
header::WWW_AUTHENTICATE,
header::HeaderValue::from_static(
"Basic realm=\"Authentication required\", charset=\"UTF-8\"",
),
));
resp_builder.body("Sorry, but you are not authorized to access this page.")
} else {
resp_builder.body(self.status.to_string())
}
}
}
pub trait StatusCodeResultExt<T, E> {
fn with_status(self, status: StatusCode) -> anyhow::Result<T>;
fn with_status_from(self, get_status: impl FnOnce(&E) -> StatusCode) -> anyhow::Result<T>;
fn with_response_status(self) -> anyhow::Result<T>
where
Self: Sized,
E: ResponseError;
}
impl<T, E> StatusCodeResultExt<T, E> for Result<T, E>
where
E: std::fmt::Display,
{
fn with_status(self, status: StatusCode) -> anyhow::Result<T> {
self.map_err(|err| anyhow::anyhow!(ErrorWithStatus { status }).context(err.to_string()))
}
fn with_status_from(self, get_status: impl FnOnce(&E) -> StatusCode) -> anyhow::Result<T> {
self.map_err(|err| {
let status = get_status(&err);
anyhow::anyhow!(ErrorWithStatus { status }).context(err.to_string())
})
}
fn with_response_status(self) -> anyhow::Result<T>
where
E: ResponseError,
{
self.with_status_from(ResponseError::status_code)
}
}
pub trait ActixErrorStatusExt<T> {
fn with_actix_error_status(self) -> anyhow::Result<T>;
}
impl<T> ActixErrorStatusExt<T> for Result<T, actix_web::Error> {
fn with_actix_error_status(self) -> anyhow::Result<T> {
self.with_status_from(|e| e.as_response_error().status_code())
}
}