use crate::metadata::FormatConfig;
use crate::registry::{
http_headers, http_status, render_graphql, render_html, render_json, render_jsonrpc,
render_text,
};
fn fallback_json(message: impl std::fmt::Display) -> serde_json::Value {
serde_json::json!({
"error": "UNKNOWN_ERROR",
"message": message.to_string()
})
}
fn fallback_html(message: impl std::fmt::Display) -> String {
format!(
"<div class=\"error\">\n<h3 class=\"error-code\">UNKNOWN_ERROR</h3>\n<p class=\"error-message\">{message}</p>\n</div>"
)
}
fn fallback_graphql(message: impl std::fmt::Display) -> serde_json::Value {
serde_json::json!({
"message": message.to_string(),
"extensions": {
"code": "UNKNOWN_ERROR"
}
})
}
fn fallback_text(message: impl std::fmt::Display) -> String {
format!("[UNKNOWN_ERROR] {message}")
}
fn fallback_jsonrpc(message: impl std::fmt::Display) -> serde_json::Value {
serde_json::json!({
"code": crate::private::DEFAULT_JSONRPC_CODE,
"message": message.to_string(),
"data": {
"diagnostic_code": "UNKNOWN_ERROR"
}
})
}
pub trait ErrorExt {
fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
fn to_html(&self, config: FormatConfig) -> String;
fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
fn to_text(&self, config: FormatConfig) -> String;
fn to_debug(&self) -> String
where
Self: std::fmt::Debug,
{
format!("{self:?}")
}
fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
fn http_status(&self) -> http::StatusCode;
fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)>;
}
impl<E: std::error::Error + 'static> ErrorExt for E {
fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
render_json(self, config).unwrap_or_else(|| Ok(fallback_json(self)))
}
fn to_html(&self, config: FormatConfig) -> String {
render_html(self, config).unwrap_or_else(|| fallback_html(self))
}
fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
render_graphql(self, config).unwrap_or_else(|| Ok(fallback_graphql(self)))
}
fn to_text(&self, config: FormatConfig) -> String {
render_text(self, config).unwrap_or_else(|| fallback_text(self))
}
fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
render_jsonrpc(self, config).unwrap_or_else(|| Ok(fallback_jsonrpc(self)))
}
fn http_status(&self) -> http::StatusCode {
http_status(self).unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR)
}
fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)> {
http_headers(self).unwrap_or_default()
}
}
pub trait HeapErrorExt {
fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
fn to_html(&self, config: FormatConfig) -> String;
fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
fn to_text(&self, config: FormatConfig) -> String;
fn to_debug(&self) -> String;
fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error>;
fn http_status(&self) -> http::StatusCode;
fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)>;
}
impl HeapErrorExt for Box<dyn std::error::Error + Send + Sync> {
fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
let error_ref: &dyn std::error::Error = self.as_ref();
if let Some(nested_arc) =
error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
{
return HeapErrorExt::to_json(nested_arc, config);
}
render_json(error_ref, config).unwrap_or_else(|| Ok(fallback_json(error_ref)))
}
fn to_html(&self, config: FormatConfig) -> String {
let error_ref: &dyn std::error::Error = self.as_ref();
if let Some(nested_arc) =
error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
{
return HeapErrorExt::to_html(nested_arc, config);
}
render_html(error_ref, config).unwrap_or_else(|| fallback_html(error_ref))
}
fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
let error_ref: &dyn std::error::Error = self.as_ref();
if let Some(nested_arc) =
error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
{
return HeapErrorExt::to_graphql(nested_arc, config);
}
render_graphql(error_ref, config).unwrap_or_else(|| Ok(fallback_graphql(error_ref)))
}
fn to_text(&self, config: FormatConfig) -> String {
let error_ref: &dyn std::error::Error = self.as_ref();
if let Some(nested_arc) =
error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
{
return HeapErrorExt::to_text(nested_arc, config);
}
render_text(error_ref, config).unwrap_or_else(|| fallback_text(error_ref))
}
fn to_debug(&self) -> String {
format!("{self:?}")
}
fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
let error_ref: &dyn std::error::Error = self.as_ref();
if let Some(nested_arc) =
error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
{
return HeapErrorExt::to_jsonrpc(nested_arc, config);
}
render_jsonrpc(error_ref, config).unwrap_or_else(|| Ok(fallback_jsonrpc(error_ref)))
}
fn http_status(&self) -> http::StatusCode {
let error_ref: &dyn std::error::Error = self.as_ref();
if let Some(nested_arc) =
error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
{
return HeapErrorExt::http_status(nested_arc);
}
http_status(error_ref).unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR)
}
fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)> {
let error_ref: &dyn std::error::Error = self.as_ref();
if let Some(nested_arc) =
error_ref.downcast_ref::<std::sync::Arc<dyn std::error::Error + Send + Sync>>()
{
return HeapErrorExt::http_headers(nested_arc);
}
http_headers(error_ref).unwrap_or_default()
}
}
impl HeapErrorExt for std::sync::Arc<dyn std::error::Error + Send + Sync> {
fn to_json(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
let error_ref: &dyn std::error::Error = self.as_ref();
render_json(error_ref, config).unwrap_or_else(|| Ok(fallback_json(error_ref)))
}
fn to_html(&self, config: FormatConfig) -> String {
let error_ref: &dyn std::error::Error = self.as_ref();
render_html(error_ref, config).unwrap_or_else(|| fallback_html(error_ref))
}
fn to_graphql(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
let error_ref: &dyn std::error::Error = self.as_ref();
render_graphql(error_ref, config).unwrap_or_else(|| Ok(fallback_graphql(error_ref)))
}
fn to_text(&self, config: FormatConfig) -> String {
let error_ref: &dyn std::error::Error = self.as_ref();
render_text(error_ref, config).unwrap_or_else(|| fallback_text(error_ref))
}
fn to_debug(&self) -> String {
format!("{self:?}")
}
fn to_jsonrpc(&self, config: FormatConfig) -> Result<serde_json::Value, serde_json::Error> {
let error_ref: &dyn std::error::Error = self.as_ref();
render_jsonrpc(error_ref, config).unwrap_or_else(|| Ok(fallback_jsonrpc(error_ref)))
}
fn http_status(&self) -> http::StatusCode {
let error_ref: &dyn std::error::Error = self.as_ref();
http_status(error_ref).unwrap_or(http::StatusCode::INTERNAL_SERVER_ERROR)
}
fn http_headers(&self) -> Vec<(http::HeaderName, http::HeaderValue)> {
let error_ref: &dyn std::error::Error = self.as_ref();
http_headers(error_ref).unwrap_or_default()
}
}