use crate::Error;
use erased_serde::Serialize as DynSerialize;
use http::StatusCode;
use serde::{Serialize, Serializer};
#[derive(Serialize)]
pub struct HttpError {
#[serde(skip)]
pub http_status_code: StatusCode,
#[serde(flatten)]
pub public: Box<dyn DynSerialize + Send + Sync>,
#[serde(skip)]
pub context: Option<String>,
}
impl HttpError {
pub fn new<S: Serialize + 'static + Send + Sync>(
http_status_code: StatusCode,
public: S,
) -> Self {
Self {
http_status_code,
public: Box::new(public),
context: None,
}
}
pub fn with_context(mut self, context: impl std::fmt::Display) -> Self {
self.context = Some(context.to_string());
self
}
pub fn with_source<E: std::error::Error + 'static + Send + Sync>(
self,
error: E,
) -> super::DomainError {
super::DomainError {
output: self,
source: Some(Box::new(error)),
}
}
}
impl From<HttpError> for Error {
fn from(value: HttpError) -> Self {
Error::Domain(Box::new(super::DomainError {
output: value,
source: None,
}))
}
}
impl PartialEq for HttpError {
fn eq(&self, other: &Self) -> bool {
self.context == other.context
&& self.http_status_code == other.http_status_code
&& serde_json::json!(self.public) == serde_json::json!(other)
}
}
#[derive(Serialize)]
pub(crate) struct HttpErrorDisplay<'s> {
#[serde(serialize_with = "serialize_http_status_code")]
pub http_status_code: http::StatusCode,
pub public: &'s dyn DynSerialize,
pub context: Option<&'s str>,
}
impl<'s> From<&'s HttpError> for HttpErrorDisplay<'s> {
fn from(value: &'s HttpError) -> Self {
Self {
http_status_code: value.http_status_code,
public: value.public.as_ref(),
context: value.context.as_deref(),
}
}
}
impl std::fmt::Display for HttpError {
fn fmt<'s>(&'s self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
serde_json::json!(HttpErrorDisplay::<'s>::from(self))
)
}
}
impl std::fmt::Debug for HttpError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "HttpError{}", self)
}
}
pub(crate) fn serialize_http_status_code<S>(
status_code: &StatusCode,
s: S,
) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
s.serialize_u16(status_code.as_u16())
}
#[cfg(test)]
mod test;