use serde::{Deserialize, Serialize};
use core::{borrow::Borrow, fmt};
use crate::alloc::{format, String, ToOwned};
#[cfg(feature = "std")]
mod error {
use serde::{Deserialize, Serialize};
use std::{error, fmt};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[non_exhaustive]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub struct TracedError {
pub message: String,
pub source: Option<Box<TracedError>>,
}
impl TracedError {
pub(super) fn new(err: &(dyn error::Error + 'static)) -> Self {
Self {
message: err.to_string(),
source: err.source().map(|source| Box::new(Self::new(source))),
}
}
}
impl fmt::Display for TracedError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.message)
}
}
impl error::Error for TracedError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
self.source
.as_ref()
.map(|source| source.as_ref() as &(dyn error::Error + 'static))
}
}
}
#[cfg(feature = "std")]
pub use self::error::TracedError;
#[derive(Clone, Serialize, Deserialize)]
#[serde(transparent)]
pub struct DebugObject(String);
impl fmt::Debug for DebugObject {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(&self.0)
}
}
impl AsRef<str> for DebugObject {
fn as_ref(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
#[non_exhaustive]
pub enum TracedValue {
Bool(bool),
Int(i128),
UInt(u128),
Float(f64),
String(String),
Object(DebugObject),
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
Error(TracedError),
}
impl TracedValue {
#[doc(hidden)] pub fn debug(object: &dyn fmt::Debug) -> Self {
Self::Object(DebugObject(format!("{object:?}")))
}
#[inline]
pub fn as_bool(&self) -> Option<bool> {
bool::from_value(self)
}
#[inline]
pub fn as_int(&self) -> Option<i128> {
i128::from_value(self)
}
#[inline]
pub fn as_uint(&self) -> Option<u128> {
u128::from_value(self)
}
#[inline]
pub fn as_float(&self) -> Option<f64> {
f64::from_value(self)
}
#[inline]
pub fn as_str(&self) -> Option<&str> {
str::from_value(self)
}
pub fn is_debug(&self, object: &dyn fmt::Debug) -> bool {
match self {
Self::Object(value) => value.0 == format!("{object:?}"),
_ => false,
}
}
pub fn as_debug_str(&self) -> Option<&str> {
match self {
Self::Object(value) => Some(&value.0),
_ => None,
}
}
#[cfg(feature = "std")]
pub(crate) fn error(err: &(dyn std::error::Error + 'static)) -> Self {
Self::Error(TracedError::new(err))
}
}
pub trait FromTracedValue<'a> {
type Output: Borrow<Self> + 'a;
fn from_value(value: &'a TracedValue) -> Option<Self::Output>;
}
impl<'a> FromTracedValue<'a> for str {
type Output = &'a str;
fn from_value(value: &'a TracedValue) -> Option<Self::Output> {
match value {
TracedValue::String(value) => Some(value),
_ => None,
}
}
}
macro_rules! impl_value_conversions {
(TracedValue :: $variant:ident ($source:ty)) => {
impl From<$source> for TracedValue {
fn from(value: $source) -> Self {
Self::$variant(value)
}
}
impl PartialEq<$source> for TracedValue {
fn eq(&self, other: &$source) -> bool {
match self {
Self::$variant(value) => value == other,
_ => false,
}
}
}
impl PartialEq<TracedValue> for $source {
fn eq(&self, other: &TracedValue) -> bool {
other == self
}
}
impl FromTracedValue<'_> for $source {
type Output = Self;
fn from_value(value: &TracedValue) -> Option<Self::Output> {
match value {
TracedValue::$variant(value) => Some(*value),
_ => None,
}
}
}
};
(TracedValue :: $variant:ident ($source:ty as $field_ty:ty)) => {
impl From<$source> for TracedValue {
fn from(value: $source) -> Self {
Self::$variant(value.into())
}
}
impl PartialEq<$source> for TracedValue {
fn eq(&self, other: &$source) -> bool {
match self {
Self::$variant(value) => *value == <$field_ty>::from(*other),
_ => false,
}
}
}
impl PartialEq<TracedValue> for $source {
fn eq(&self, other: &TracedValue) -> bool {
other == self
}
}
impl FromTracedValue<'_> for $source {
type Output = Self;
fn from_value(value: &TracedValue) -> Option<Self::Output> {
match value {
TracedValue::$variant(value) => (*value).try_into().ok(),
_ => None,
}
}
}
};
}
impl_value_conversions!(TracedValue::Bool(bool));
impl_value_conversions!(TracedValue::Int(i128));
impl_value_conversions!(TracedValue::Int(i64 as i128));
impl_value_conversions!(TracedValue::UInt(u128));
impl_value_conversions!(TracedValue::UInt(u64 as u128));
impl_value_conversions!(TracedValue::Float(f64));
impl PartialEq<str> for TracedValue {
fn eq(&self, other: &str) -> bool {
match self {
Self::String(value) => value == other,
_ => false,
}
}
}
impl PartialEq<TracedValue> for str {
fn eq(&self, other: &TracedValue) -> bool {
other == self
}
}
impl From<&str> for TracedValue {
fn from(value: &str) -> Self {
Self::String(value.to_owned())
}
}
impl PartialEq<&str> for TracedValue {
fn eq(&self, other: &&str) -> bool {
match self {
Self::String(value) => value == *other,
_ => false,
}
}
}
impl PartialEq<TracedValue> for &str {
fn eq(&self, other: &TracedValue) -> bool {
other == self
}
}