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
    }
}