use std::fmt::Display;
use axum::http::StatusCode;
use crate::utils::path::ResolveTargetError;
pub trait OrStatus<T> {
fn or_status(self, status: StatusCode, msg: &str) -> Result<T, StatusCode>
where
Self: Sized;
fn or_400(self, msg: &str) -> Result<T, StatusCode>
where
Self: Sized,
{
self.or_status(StatusCode::BAD_REQUEST, msg)
}
fn or_403(self, msg: &str) -> Result<T, StatusCode>
where
Self: Sized,
{
self.or_status(StatusCode::FORBIDDEN, msg)
}
fn or_404(self, msg: &str) -> Result<T, StatusCode>
where
Self: Sized,
{
self.or_status(StatusCode::NOT_FOUND, msg)
}
#[allow(dead_code)]
fn or_409(self, msg: &str) -> Result<T, StatusCode>
where
Self: Sized,
{
self.or_status(StatusCode::CONFLICT, msg)
}
fn or_500(self, msg: &str) -> Result<T, StatusCode>
where
Self: Sized,
{
self.or_status(StatusCode::INTERNAL_SERVER_ERROR, msg)
}
#[allow(dead_code)]
fn or_503(self, msg: &str) -> Result<T, StatusCode>
where
Self: Sized,
{
self.or_status(StatusCode::SERVICE_UNAVAILABLE, msg)
}
}
impl<T, E: Display> OrStatus<T> for Result<T, E> {
fn or_status(self, status: StatusCode, msg: &str) -> Result<T, StatusCode> {
self.map_err(|e| {
if status.is_server_error() {
tracing::error!(error = %e, "{msg}");
} else {
tracing::debug!(error = %e, "{msg}");
}
status
})
}
}
impl<T> OrStatus<T> for Option<T> {
fn or_status(self, status: StatusCode, msg: &str) -> Result<T, StatusCode> {
self.ok_or_else(|| {
if status.is_server_error() {
tracing::error!("{msg}");
} else {
tracing::debug!("{msg}");
}
status
})
}
}
pub trait IntoResolved<T> {
fn or_invalid(self, on_invalid: StatusCode) -> Result<T, StatusCode>;
}
impl<T> IntoResolved<T> for Result<T, ResolveTargetError> {
fn or_invalid(self, on_invalid: StatusCode) -> Result<T, StatusCode> {
if let Err(e) = self.as_ref() {
tracing::debug!(error = ?e, "path resolution failed");
}
self.map_err(|e| e.status(on_invalid))
}
}