use crate::{Slice, Value, Visit}; use core::fmt; use core::num::Wrapping; /// A type that can be converted to a [`Value`]. /// /// `Valuable` types are inspected by defining a [`Visit`] implementation and /// using it when calling [`Valuable::visit`]. See [`Visit`] documentation for /// more details. /// /// The `Valuable` procedural macro makes implementing `Valuable` easy. Users /// can add add [`#[derive(Valuable)]`][macro] to their types. /// /// `Valuable` provides implementations for many Rust primitives and standard /// library types. /// /// Types implementing `Valuable` may also implement one of the more specific /// traits: [`Structable`], [`Enumerable`], [`Listable`], and [`Mappable`]. These traits /// should be implemented when the type is a nested container of other `Valuable` types. /// /// [`Value`]: Value /// [`Visit`]: Visit /// [`Valuable::visit`]: Valuable::visit /// [`Structable`]: crate::Structable /// [`Enumerable`]: crate::Enumerable /// [`Listable`]: crate::Listable /// [`Mappable`]: crate::Mappable /// [macro]: macro@crate::Valuable pub trait Valuable { /// Converts self into a [`Value`] instance. /// /// # Examples /// /// ``` /// use valuable::Valuable; /// /// let _ = "hello".as_value(); /// ``` fn as_value(&self) -> Value<'_>; /// Calls the relevant method on [`Visit`] to extract data from `self`. /// /// This method is used to extract type-specific data from the value and is /// intended to be an implementation detail. For example, `Vec` implements /// `visit` by calling [`visit_value()`] on each of its elements. Structs /// implement `visit` by calling [`visit_named_fields()`] or /// [`visit_unnamed_fields()`]. /// /// Usually, users will call the [`visit`] function instead. /// /// [`Visit`]: Visit /// [`visit`]: visit() /// [`visit_value()`]: Visit::visit_value() /// [`visit_named_fields()`]: Visit::visit_named_fields() /// [`visit_unnamed_fields()`]: Visit::visit_unnamed_fields() fn visit(&self, visit: &mut dyn Visit); /// Calls [`Visit::visit_primitive_slice()`] with `self`. /// /// This method is an implementation detail used to optimize visiting /// primitive slices. /// /// [`Visit::visit_primitive_slice()`]: Visit::visit_primitive_slice fn visit_slice(slice: &[Self], visit: &mut dyn Visit) where Self: Sized, { for item in slice { visit.visit_value(item.as_value()); } } } macro_rules! deref { ( $( $(#[$attrs:meta])* $ty:ty, )* ) => { $( $(#[$attrs])* impl Valuable for $ty { fn as_value(&self) -> Value<'_> { T::as_value(&**self) } fn visit(&self, visit: &mut dyn Visit) { T::visit(&**self, visit); } } )* }; } deref! { &T, &mut T, #[cfg(feature = "alloc")] alloc::boxed::Box, #[cfg(feature = "alloc")] alloc::rc::Rc, #[cfg(not(valuable_no_atomic_cas))] #[cfg(feature = "alloc")] alloc::sync::Arc, } macro_rules! valuable { ( $( $variant:ident($ty:ty), )* ) => { $( impl Valuable for $ty { fn as_value(&self) -> Value<'_> { Value::$variant(*self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(self.as_value()); } fn visit_slice(slice: &[Self], visit: &mut dyn Visit) where Self: Sized, { visit.visit_primitive_slice(Slice::$variant(slice)); } } )* }; } valuable! { Bool(bool), Char(char), F32(f32), F64(f64), I8(i8), I16(i16), I32(i32), I64(i64), I128(i128), Isize(isize), U8(u8), U16(u16), U32(u32), U64(u64), U128(u128), Usize(usize), } macro_rules! nonzero { ( $( $variant:ident($ty:ident), )* ) => { $( impl Valuable for core::num::$ty { fn as_value(&self) -> Value<'_> { Value::$variant(self.get()) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(self.as_value()); } } )* }; } nonzero! { I8(NonZeroI8), I16(NonZeroI16), I32(NonZeroI32), I64(NonZeroI64), I128(NonZeroI128), Isize(NonZeroIsize), U8(NonZeroU8), U16(NonZeroU16), U32(NonZeroU32), U64(NonZeroU64), U128(NonZeroU128), Usize(NonZeroUsize), } #[cfg(not(valuable_no_atomic))] macro_rules! atomic { ( $( $(#[$attrs:meta])* $variant:ident($ty:ident), )* ) => { $( $(#[$attrs])* impl Valuable for core::sync::atomic::$ty { fn as_value(&self) -> Value<'_> { // Use SeqCst to match Debug and serde which use SeqCst. // https://github.com/rust-lang/rust/blob/1.52.1/library/core/src/sync/atomic.rs#L1361-L1366 // https://github.com/serde-rs/serde/issues/1496 Value::$variant(self.load(core::sync::atomic::Ordering::SeqCst)) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(self.as_value()); } } )* }; } #[cfg(not(valuable_no_atomic))] atomic! { Bool(AtomicBool), I8(AtomicI8), I16(AtomicI16), I32(AtomicI32), #[cfg(not(valuable_no_atomic_64))] I64(AtomicI64), Isize(AtomicIsize), U8(AtomicU8), U16(AtomicU16), U32(AtomicU32), #[cfg(not(valuable_no_atomic_64))] U64(AtomicU64), Usize(AtomicUsize), } impl Valuable for Wrapping { fn as_value(&self) -> Value<'_> { self.0.as_value() } fn visit(&self, visit: &mut dyn Visit) { self.0.visit(visit); } } impl Valuable for () { fn as_value(&self) -> Value<'_> { Value::Tuplable(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_unnamed_fields(&[]); } } impl Valuable for Option { fn as_value(&self) -> Value<'_> { match self { Some(v) => v.as_value(), None => Value::Unit, } } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(self.as_value()); } } impl Valuable for &'_ str { fn as_value(&self) -> Value<'_> { Value::String(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(Value::String(self)); } fn visit_slice(slice: &[Self], visit: &mut dyn Visit) where Self: Sized, { visit.visit_primitive_slice(Slice::Str(slice)); } } #[cfg(feature = "alloc")] impl Valuable for alloc::string::String { fn as_value(&self) -> Value<'_> { Value::String(&self[..]) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(Value::String(self)); } fn visit_slice(slice: &[Self], visit: &mut dyn Visit) where Self: Sized, { visit.visit_primitive_slice(Slice::String(slice)); } } #[cfg(feature = "std")] impl Valuable for &std::path::Path { fn as_value(&self) -> Value<'_> { Value::Path(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(Value::Path(self)); } } #[cfg(feature = "std")] impl Valuable for std::path::PathBuf { fn as_value(&self) -> Value<'_> { Value::Path(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(Value::Path(self)); } } #[cfg(feature = "std")] impl Valuable for dyn std::error::Error + 'static { fn as_value(&self) -> Value<'_> { Value::Error(self) } fn visit(&self, visit: &mut dyn Visit) { visit.visit_value(self.as_value()); } } impl fmt::Debug for dyn Valuable + '_ { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let value = self.as_value(); value.fmt(fmt) } }