valuable/
valuable.rs

1use crate::{Slice, Value, Visit};
2
3use core::fmt;
4use core::num::Wrapping;
5
6/// A type that can be converted to a [`Value`].
7///
8/// `Valuable` types are inspected by defining a [`Visit`] implementation and
9/// using it when calling [`Valuable::visit`]. See [`Visit`] documentation for
10/// more details.
11///
12/// The `Valuable` procedural macro makes implementing `Valuable` easy. Users
13/// can add add [`#[derive(Valuable)]`][macro] to their types.
14///
15/// `Valuable` provides implementations for many Rust primitives and standard
16/// library types.
17///
18/// Types implementing `Valuable` may also implement one of the more specific
19/// traits: [`Structable`], [`Enumerable`], [`Listable`], and [`Mappable`]. These traits
20/// should be implemented when the type is a nested container of other `Valuable` types.
21///
22/// [`Value`]: Value
23/// [`Visit`]: Visit
24/// [`Valuable::visit`]: Valuable::visit
25/// [`Structable`]: crate::Structable
26/// [`Enumerable`]: crate::Enumerable
27/// [`Listable`]: crate::Listable
28/// [`Mappable`]: crate::Mappable
29/// [macro]: macro@crate::Valuable
30pub trait Valuable {
31    /// Converts self into a [`Value`] instance.
32    ///
33    /// # Examples
34    ///
35    /// ```
36    /// use valuable::Valuable;
37    ///
38    /// let _ = "hello".as_value();
39    /// ```
40    fn as_value(&self) -> Value<'_>;
41
42    /// Calls the relevant method on [`Visit`] to extract data from `self`.
43    ///
44    /// This method is used to extract type-specific data from the value and is
45    /// intended to be an implementation detail. For example, `Vec` implements
46    /// `visit` by calling [`visit_value()`] on each of its elements. Structs
47    /// implement `visit` by calling [`visit_named_fields()`] or
48    /// [`visit_unnamed_fields()`].
49    ///
50    /// Usually, users will call the [`visit`] function instead.
51    ///
52    /// [`Visit`]: Visit
53    /// [`visit`]: visit()
54    /// [`visit_value()`]: Visit::visit_value()
55    /// [`visit_named_fields()`]: Visit::visit_named_fields()
56    /// [`visit_unnamed_fields()`]: Visit::visit_unnamed_fields()
57    fn visit(&self, visit: &mut dyn Visit);
58
59    /// Calls [`Visit::visit_primitive_slice()`] with `self`.
60    ///
61    /// This method is an implementation detail used to optimize visiting
62    /// primitive slices.
63    ///
64    /// [`Visit::visit_primitive_slice()`]: Visit::visit_primitive_slice
65    fn visit_slice(slice: &[Self], visit: &mut dyn Visit)
66    where
67        Self: Sized,
68    {
69        for item in slice {
70            visit.visit_value(item.as_value());
71        }
72    }
73}
74
75macro_rules! deref {
76    (
77        $(
78            $(#[$attrs:meta])*
79            $ty:ty,
80        )*
81    ) => {
82        $(
83            $(#[$attrs])*
84            impl<T: ?Sized + Valuable> Valuable for $ty {
85                fn as_value(&self) -> Value<'_> {
86                    T::as_value(&**self)
87                }
88
89                fn visit(&self, visit: &mut dyn Visit) {
90                    T::visit(&**self, visit);
91                }
92            }
93        )*
94    };
95}
96
97deref! {
98    &T,
99    &mut T,
100    #[cfg(feature = "alloc")]
101    alloc::boxed::Box<T>,
102    #[cfg(feature = "alloc")]
103    alloc::rc::Rc<T>,
104    #[cfg(not(valuable_no_atomic_cas))]
105    #[cfg(feature = "alloc")]
106    alloc::sync::Arc<T>,
107}
108
109macro_rules! valuable {
110    (
111        $(
112            $variant:ident($ty:ty),
113        )*
114    ) => {
115        $(
116            impl Valuable for $ty {
117                fn as_value(&self) -> Value<'_> {
118                    Value::$variant(*self)
119                }
120
121                fn visit(&self, visit: &mut dyn Visit) {
122                    visit.visit_value(self.as_value());
123                }
124
125                fn visit_slice(slice: &[Self], visit: &mut dyn Visit)
126                where
127                    Self: Sized,
128                {
129                    visit.visit_primitive_slice(Slice::$variant(slice));
130                }
131            }
132        )*
133    };
134}
135
136valuable! {
137    Bool(bool),
138    Char(char),
139    F32(f32),
140    F64(f64),
141    I8(i8),
142    I16(i16),
143    I32(i32),
144    I64(i64),
145    I128(i128),
146    Isize(isize),
147    U8(u8),
148    U16(u16),
149    U32(u32),
150    U64(u64),
151    U128(u128),
152    Usize(usize),
153}
154
155macro_rules! nonzero {
156    (
157        $(
158            $variant:ident($ty:ident),
159        )*
160    ) => {
161        $(
162            impl Valuable for core::num::$ty {
163                fn as_value(&self) -> Value<'_> {
164                    Value::$variant(self.get())
165                }
166
167                fn visit(&self, visit: &mut dyn Visit) {
168                    visit.visit_value(self.as_value());
169                }
170            }
171        )*
172    };
173}
174
175nonzero! {
176    I8(NonZeroI8),
177    I16(NonZeroI16),
178    I32(NonZeroI32),
179    I64(NonZeroI64),
180    I128(NonZeroI128),
181    Isize(NonZeroIsize),
182    U8(NonZeroU8),
183    U16(NonZeroU16),
184    U32(NonZeroU32),
185    U64(NonZeroU64),
186    U128(NonZeroU128),
187    Usize(NonZeroUsize),
188}
189
190#[cfg(not(valuable_no_atomic))]
191macro_rules! atomic {
192    (
193        $(
194            $(#[$attrs:meta])*
195            $variant:ident($ty:ident),
196        )*
197    ) => {
198        $(
199            $(#[$attrs])*
200            impl Valuable for core::sync::atomic::$ty {
201                fn as_value(&self) -> Value<'_> {
202                    // Use SeqCst to match Debug and serde which use SeqCst.
203                    // https://github.com/rust-lang/rust/blob/1.52.1/library/core/src/sync/atomic.rs#L1361-L1366
204                    // https://github.com/serde-rs/serde/issues/1496
205                    Value::$variant(self.load(core::sync::atomic::Ordering::SeqCst))
206                }
207
208                fn visit(&self, visit: &mut dyn Visit) {
209                    visit.visit_value(self.as_value());
210                }
211            }
212        )*
213    };
214}
215
216#[cfg(not(valuable_no_atomic))]
217atomic! {
218    Bool(AtomicBool),
219    I8(AtomicI8),
220    I16(AtomicI16),
221    I32(AtomicI32),
222    #[cfg(not(valuable_no_atomic_64))]
223    I64(AtomicI64),
224    Isize(AtomicIsize),
225    U8(AtomicU8),
226    U16(AtomicU16),
227    U32(AtomicU32),
228    #[cfg(not(valuable_no_atomic_64))]
229    U64(AtomicU64),
230    Usize(AtomicUsize),
231}
232
233impl<T: Valuable> Valuable for Wrapping<T> {
234    fn as_value(&self) -> Value<'_> {
235        self.0.as_value()
236    }
237
238    fn visit(&self, visit: &mut dyn Visit) {
239        self.0.visit(visit);
240    }
241}
242
243impl Valuable for () {
244    fn as_value(&self) -> Value<'_> {
245        Value::Tuplable(self)
246    }
247
248    fn visit(&self, visit: &mut dyn Visit) {
249        visit.visit_unnamed_fields(&[]);
250    }
251}
252
253impl<T: Valuable> Valuable for Option<T> {
254    fn as_value(&self) -> Value<'_> {
255        match self {
256            Some(v) => v.as_value(),
257            None => Value::Unit,
258        }
259    }
260
261    fn visit(&self, visit: &mut dyn Visit) {
262        visit.visit_value(self.as_value());
263    }
264}
265
266impl Valuable for &'_ str {
267    fn as_value(&self) -> Value<'_> {
268        Value::String(self)
269    }
270
271    fn visit(&self, visit: &mut dyn Visit) {
272        visit.visit_value(Value::String(self));
273    }
274
275    fn visit_slice(slice: &[Self], visit: &mut dyn Visit)
276    where
277        Self: Sized,
278    {
279        visit.visit_primitive_slice(Slice::Str(slice));
280    }
281}
282
283#[cfg(feature = "alloc")]
284impl Valuable for alloc::string::String {
285    fn as_value(&self) -> Value<'_> {
286        Value::String(&self[..])
287    }
288
289    fn visit(&self, visit: &mut dyn Visit) {
290        visit.visit_value(Value::String(self));
291    }
292
293    fn visit_slice(slice: &[Self], visit: &mut dyn Visit)
294    where
295        Self: Sized,
296    {
297        visit.visit_primitive_slice(Slice::String(slice));
298    }
299}
300
301#[cfg(feature = "std")]
302impl Valuable for &std::path::Path {
303    fn as_value(&self) -> Value<'_> {
304        Value::Path(self)
305    }
306
307    fn visit(&self, visit: &mut dyn Visit) {
308        visit.visit_value(Value::Path(self));
309    }
310}
311
312#[cfg(feature = "std")]
313impl Valuable for std::path::PathBuf {
314    fn as_value(&self) -> Value<'_> {
315        Value::Path(self)
316    }
317
318    fn visit(&self, visit: &mut dyn Visit) {
319        visit.visit_value(Value::Path(self));
320    }
321}
322
323#[cfg(feature = "std")]
324impl Valuable for dyn std::error::Error + 'static {
325    fn as_value(&self) -> Value<'_> {
326        Value::Error(self)
327    }
328
329    fn visit(&self, visit: &mut dyn Visit) {
330        visit.visit_value(self.as_value());
331    }
332}
333
334impl fmt::Debug for dyn Valuable + '_ {
335    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
336        let value = self.as_value();
337        value.fmt(fmt)
338    }
339}