use serde::{Deserialize, Serialize};
use std::any::type_name;
use crate::{deserializer::DeserializeError, Error};
#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Diagnostic {
pub error_type: String,
pub error_message: String,
}
impl From<DeserializeError> for Diagnostic {
fn from(value: DeserializeError) -> Self {
Diagnostic {
error_type: type_name_of_val(&value),
error_message: value.to_string(),
}
}
}
impl From<Error> for Diagnostic {
fn from(value: Error) -> Self {
Diagnostic {
error_type: type_name_of_val(&value),
error_message: value.to_string(),
}
}
}
impl From<Box<dyn std::error::Error>> for Diagnostic {
fn from(value: Box<dyn std::error::Error>) -> Self {
Diagnostic {
error_type: type_name_of_val(&value),
error_message: value.to_string(),
}
}
}
impl From<std::convert::Infallible> for Diagnostic {
fn from(value: std::convert::Infallible) -> Self {
Diagnostic {
error_type: type_name_of_val(&value),
error_message: value.to_string(),
}
}
}
impl From<String> for Diagnostic {
fn from(value: String) -> Self {
Diagnostic {
error_type: type_name_of_val(&value),
error_message: value.to_string(),
}
}
}
impl From<&'static str> for Diagnostic {
fn from(value: &'static str) -> Self {
Diagnostic {
error_type: type_name_of_val(&value),
error_message: value.to_string(),
}
}
}
impl From<std::io::Error> for Diagnostic {
fn from(value: std::io::Error) -> Self {
Diagnostic {
error_type: type_name_of_val(&value),
error_message: value.to_string(),
}
}
}
#[cfg(feature = "anyhow")]
#[cfg_attr(docsrs, doc(cfg(feature = "anyhow")))]
impl From<anyhow::Error> for Diagnostic {
fn from(value: anyhow::Error) -> Diagnostic {
Diagnostic {
error_type: type_name_of_val(&value),
error_message: value.to_string(),
}
}
}
#[cfg(feature = "eyre")]
#[cfg_attr(docsrs, doc(cfg(feature = "eyre")))]
impl From<eyre::Report> for Diagnostic {
fn from(value: eyre::Report) -> Diagnostic {
Diagnostic {
error_type: type_name_of_val(&value),
error_message: value.to_string(),
}
}
}
#[cfg(feature = "miette")]
#[cfg_attr(docsrs, doc(cfg(feature = "miette")))]
impl From<miette::Report> for Diagnostic {
fn from(value: miette::Report) -> Diagnostic {
Diagnostic {
error_type: type_name_of_val(&value),
error_message: value.to_string(),
}
}
}
pub(crate) fn type_name_of_val<T>(_: T) -> String {
type_name::<T>().into()
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn round_trip_lambda_error() {
use serde_json::{json, Value};
let expected = json!({
"errorType": "InvalidEventDataError",
"errorMessage": "Error parsing event data.",
});
let actual = Diagnostic {
error_type: "InvalidEventDataError".into(),
error_message: "Error parsing event data.".into(),
};
let actual: Value = serde_json::to_value(actual).expect("failed to serialize diagnostic");
assert_eq!(expected, actual);
}
#[cfg(feature = "anyhow")]
#[test]
fn test_anyhow_integration() {
use anyhow::Error as AnyhowError;
let error: AnyhowError = anyhow::anyhow!("anyhow error");
let diagnostic: Diagnostic = error.into();
assert_eq!(diagnostic.error_type, "&anyhow::Error");
assert_eq!(diagnostic.error_message, "anyhow error");
}
#[cfg(feature = "eyre")]
#[test]
fn test_eyre_integration() {
use eyre::Report;
let error: Report = eyre::eyre!("eyre error");
let diagnostic: Diagnostic = error.into();
assert_eq!(diagnostic.error_type, "&eyre::Report");
assert_eq!(diagnostic.error_message, "eyre error");
}
#[cfg(feature = "miette")]
#[test]
fn test_miette_integration() {
use miette::Report;
let error: Report = miette::miette!("miette error");
let diagnostic: Diagnostic = error.into();
assert_eq!(diagnostic.error_type, "&miette::eyreish::Report");
assert_eq!(diagnostic.error_message, "miette error");
}
}