use serde::{Deserialize, Serialize};
use std::fmt;
use super::ErrorCode;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Error {
#[serde(flatten)]
err: Box<ErrorImpl>,
}
#[derive(Debug, Serialize, Deserialize)]
struct ErrorImpl {
code: ErrorCode,
#[serde(rename = "type")]
error_type: String,
message: String,
#[serde(skip_serializing_if = "Option::is_none")]
data: Option<serde_json::Value>,
#[serde(skip)]
source: Option<Box<dyn std::error::Error + Send + Sync>>,
#[serde(skip)]
#[cfg(debug_assertions)]
backtrace: Option<std::backtrace::Backtrace>,
}
impl Clone for ErrorImpl {
fn clone(&self) -> Self {
Self {
code: self.code,
error_type: self.error_type.clone(),
message: self.message.clone(),
data: self.data.clone(),
source: None,
#[cfg(debug_assertions)]
backtrace: None,
}
}
}
impl Error {
pub fn new(code: ErrorCode, error_type: impl Into<String>, message: impl Into<String>) -> Self {
Self {
err: Box::new(ErrorImpl {
code,
error_type: error_type.into(),
message: message.into(),
data: None,
source: None,
#[cfg(debug_assertions)]
backtrace: Some(std::backtrace::Backtrace::capture()),
}),
}
}
pub fn with_data<T: Serialize>(self, data: T) -> Self {
let mut err = self.err;
err.data = serde_json::to_value(data).ok();
Self { err }
}
pub fn with_source<E>(self, source: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
let mut err = self.err;
err.source = Some(Box::new(source));
Self { err }
}
pub fn internal<E>(error: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::new(ErrorCode::Internal, "internal_error", error.to_string()).with_source(error)
}
pub fn code(&self) -> ErrorCode {
self.err.code
}
pub fn error_type(&self) -> &str {
&self.err.error_type
}
pub fn message(&self) -> &str {
&self.err.message
}
pub fn data(&self) -> Option<&serde_json::Value> {
self.err.data.as_ref()
}
pub fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self
.err
.source
.as_ref()
.map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
}
#[cfg(debug_assertions)]
pub fn backtrace(&self) -> Option<&std::backtrace::Backtrace> {
self.err.backtrace.as_ref()
}
pub fn is_code(&self, code: ErrorCode) -> bool {
self.err.code == code
}
pub fn is<T: super::TypedError>(&self, _error_type: T) -> bool {
self.err.error_type == T::TYPE
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"[{}] {}: {}",
self.err.error_type, self.err.code, self.err.message
)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self
.err
.source
.as_ref()
.map(|e| e.as_ref() as &(dyn std::error::Error + 'static))
}
}