blob: e8fa5ddcf026e00ac7f40534b695617af4b8c3ac [file] [log] [blame] [edit]
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<T: ?Sized + Valuable> 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<T>,
#[cfg(feature = "alloc")]
alloc::rc::Rc<T>,
#[cfg(not(valuable_no_atomic_cas))]
#[cfg(feature = "alloc")]
alloc::sync::Arc<T>,
}
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<T: Valuable> Valuable for Wrapping<T> {
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<T: Valuable> Valuable for Option<T> {
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)
}
}