valuable/
structable.rs

1use crate::field::*;
2use crate::*;
3
4use core::fmt;
5
6/// A struct-like [`Valuable`] sub-type.
7///
8/// Implemented by [`Valuable`] types that have a struct-like shape. Fields may
9/// be named or unnamed (tuple). Values that implement `Structable` must return
10/// [`Value::Structable`] from their [`Valuable::as_value`] implementation.
11///
12/// # Inspecting
13///
14/// Inspecting fields contained by a `Structable` instance is done by visiting
15/// the struct. When visiting a `Structable`, either the `visit_named_fields()`
16/// or the `visit_unnamed_fields()` methods of `Visit` are called. Each method
17/// may be called multiple times per `Structable`, but the two methods are never
18/// mixed.
19///
20/// ```
21/// use valuable::{NamedValues, Valuable, Value, Visit};
22///
23/// #[derive(Valuable)]
24/// struct MyStruct {
25///     foo: u32,
26///     bar: u32,
27/// }
28///
29/// struct PrintFields;
30///
31/// impl Visit for PrintFields {
32///     fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) {
33///         for (field, value) in named_values.iter() {
34///             println!("{}: {:?}", field.name(), value);
35///         }
36///     }
37///
38///     fn visit_value(&mut self, value: Value<'_>) {
39///         match value {
40///             Value::Structable(v) => v.visit(self),
41///             _ => {} // do nothing for other types
42///         }
43///     }
44/// }
45///
46/// let my_struct = MyStruct {
47///     foo: 123,
48///     bar: 456,
49/// };
50///
51/// valuable::visit(&my_struct, &mut PrintFields);
52/// ```
53///
54/// If the struct is **statically** defined, then all fields are known ahead of
55/// time and may be accessed via the [`StructDef`] instance returned by
56/// [`definition()`]. [`NamedField`] instances returned by [`definition()`]
57/// maybe used to efficiently extract specific field values.
58///
59/// # Implementing
60///
61/// Implementing `Structable` is usually done by adding `#[derive(Valuable)]` to
62/// a Rust `struct` definition.
63///
64/// ```
65/// use valuable::{Fields, Valuable, Structable, StructDef};
66///
67/// #[derive(Valuable)]
68/// struct MyStruct {
69///     foo: &'static str,
70/// }
71///
72/// let my_struct = MyStruct { foo: "Hello" };
73/// let fields = match my_struct.definition() {
74///     StructDef::Static { name, fields, .. } => {
75///         assert_eq!("MyStruct", name);
76///         fields
77///     }
78///     _ => unreachable!(),
79/// };
80///
81/// match fields {
82///     Fields::Named(named_fields) => {
83///         assert_eq!(1, named_fields.len());
84///         assert_eq!("foo", named_fields[0].name());
85///     }
86///     _ => unreachable!(),
87/// }
88/// ```
89///
90/// [`definition()`]: Structable::definition()
91pub trait Structable: Valuable {
92    /// Returns the struct's definition.
93    ///
94    /// See [`StructDef`] documentation for more details.
95    ///
96    /// # Examples
97    ///
98    /// ```
99    /// use valuable::{Structable, Valuable};
100    ///
101    /// #[derive(Valuable)]
102    /// struct MyStruct {
103    ///     foo: u32,
104    /// }
105    ///
106    /// let my_struct = MyStruct {
107    ///     foo: 123,
108    /// };
109    ///
110    /// assert_eq!("MyStruct", my_struct.definition().name());
111    fn definition(&self) -> StructDef<'_>;
112}
113
114/// A struct's name, fields, and other struct-level information.
115///
116/// Returned by [`Structable::definition()`], `StructDef` provides the caller
117/// with information about the struct's definition.
118///
119/// [`Structable::definition()`]: Structable::definition
120#[derive(Debug)]
121#[non_exhaustive]
122pub enum StructDef<'a> {
123    /// The struct is statically-defined, all fields are known ahead of time.
124    ///
125    /// Most `Structable` definitions for Rust struct types will be
126    /// `StructDef::Static`.
127    ///
128    /// # Examples
129    ///
130    /// A statically defined struct
131    ///
132    /// ```
133    /// use valuable::{Fields, Valuable, Structable, StructDef};
134    ///
135    /// #[derive(Valuable)]
136    /// struct MyStruct {
137    ///     foo: &'static str,
138    /// }
139    ///
140    /// let my_struct = MyStruct { foo: "Hello" };
141    /// let fields = match my_struct.definition() {
142    ///     StructDef::Static { name, fields, ..} => {
143    ///         assert_eq!("MyStruct", name);
144    ///         fields
145    ///     }
146    ///     _ => unreachable!(),
147    /// };
148    ///
149    /// match fields {
150    ///     Fields::Named(named_fields) => {
151    ///         assert_eq!(1, named_fields.len());
152    ///         assert_eq!("foo", named_fields[0].name());
153    ///     }
154    ///     _ => unreachable!(),
155    /// }
156    /// ```
157    #[non_exhaustive]
158    Static {
159        /// The struct's name.
160        name: &'static str,
161
162        /// The struct's fields.
163        fields: Fields<'static>,
164    },
165
166    /// The struct is dynamically-defined, not all fields are known ahead of
167    /// time.
168    ///
169    /// A dynamically-defined struct **could** be represented using
170    /// [`Mappable`], though, using `Structable` offers benefits in a couple of
171    /// cases. For example, when serializing a `Value`, some formats will
172    /// serialize maps and structs differently. In this case, differentiating
173    /// the two is required. There also are times when **some** struct fields
174    /// are known statically, but not all of them (see second example).
175    ///
176    /// # Examples
177    ///
178    /// The struct stores field values in a `HashMap`.
179    ///
180    /// ```
181    /// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit};
182    /// use std::collections::HashMap;
183    ///
184    /// /// A dynamic struct
185    /// struct Dyn {
186    ///     // The struct name
187    ///     name: String,
188    ///
189    ///     // Named values.
190    ///     values: HashMap<String, Box<dyn Valuable>>,
191    /// }
192    ///
193    /// impl Valuable for Dyn {
194    ///     fn as_value(&self) -> Value<'_> {
195    ///         Value::Structable(self)
196    ///     }
197    ///
198    ///     fn visit(&self, visit: &mut dyn Visit) {
199    ///         // This could be optimized to batch some.
200    ///         for (field, value) in self.values.iter() {
201    ///             visit.visit_named_fields(&NamedValues::new(
202    ///                 &[NamedField::new(field)],
203    ///                 &[value.as_value()],
204    ///             ));
205    ///         }
206    ///     }
207    /// }
208    ///
209    /// impl Structable for Dyn {
210    ///     fn definition(&self) -> StructDef<'_> {
211    ///         StructDef::new_dynamic(&self.name, Fields::Named(&[]))
212    ///     }
213    /// }
214    /// ```
215    ///
216    /// Some fields are known statically.
217    ///
218    /// ```
219    /// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit};
220    /// use std::collections::HashMap;
221    ///
222    /// struct HalfStatic {
223    ///     foo: u32,
224    ///     bar: u32,
225    ///     extra_values: HashMap<String, Box<dyn Valuable>>,
226    /// }
227    ///
228    /// impl Valuable for HalfStatic {
229    ///     fn as_value(&self) -> Value<'_> {
230    ///         Value::Structable(self)
231    ///     }
232    ///
233    ///     fn visit(&self, visit: &mut dyn Visit) {
234    ///         // First, visit static fields
235    ///         visit.visit_named_fields(&NamedValues::new(
236    ///             FIELDS,
237    ///             &[self.foo.as_value(), self.bar.as_value()],
238    ///         ));
239    ///
240    ///         // This could be optimized to batch some.
241    ///         for (field, value) in self.extra_values.iter() {
242    ///             visit.visit_named_fields(&NamedValues::new(
243    ///                 &[NamedField::new(field)],
244    ///                 &[value.as_value()],
245    ///             ));
246    ///         }
247    ///     }
248    /// }
249    ///
250    /// static FIELDS: &[NamedField<'static>] = &[
251    ///     NamedField::new("foo"),
252    ///     NamedField::new("bar"),
253    /// ];
254    ///
255    /// impl Structable for HalfStatic {
256    ///     fn definition(&self) -> StructDef<'_> {
257    ///         // Include known fields.
258    ///         StructDef::new_dynamic(
259    ///             "HalfStatic",
260    ///             Fields::Named(FIELDS))
261    ///     }
262    /// }
263    /// ```
264    #[non_exhaustive]
265    Dynamic {
266        /// The struct's name
267        name: &'a str,
268
269        /// The struct's fields.
270        fields: Fields<'a>,
271    },
272}
273
274impl fmt::Debug for dyn Structable + '_ {
275    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
276        let def = self.definition();
277
278        if def.fields().is_named() {
279            struct DebugStruct<'a, 'b> {
280                fmt: fmt::DebugStruct<'a, 'b>,
281            }
282
283            let mut debug = DebugStruct {
284                fmt: fmt.debug_struct(def.name()),
285            };
286
287            impl Visit for DebugStruct<'_, '_> {
288                fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) {
289                    for (field, value) in named_values {
290                        self.fmt.field(field.name(), value);
291                    }
292                }
293
294                fn visit_value(&mut self, _: Value<'_>) {
295                    unreachable!()
296                }
297            }
298
299            self.visit(&mut debug);
300
301            debug.fmt.finish()
302        } else {
303            struct DebugStruct<'a, 'b> {
304                fmt: fmt::DebugTuple<'a, 'b>,
305            }
306
307            let mut debug = DebugStruct {
308                fmt: fmt.debug_tuple(def.name()),
309            };
310
311            impl Visit for DebugStruct<'_, '_> {
312                fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) {
313                    for value in values {
314                        self.fmt.field(value);
315                    }
316                }
317
318                fn visit_value(&mut self, _: Value<'_>) {
319                    unreachable!();
320                }
321            }
322
323            self.visit(&mut debug);
324
325            debug.fmt.finish()
326        }
327    }
328}
329
330impl<'a> StructDef<'a> {
331    /// Create a new [`StructDef::Static`] instance.
332    ///
333    /// This should be used when a struct's fields are fixed and known ahead of time.
334    ///
335    /// # Examples
336    ///
337    /// ```
338    /// use valuable::{StructDef, Fields};
339    ///
340    /// let def = StructDef::new_static("Foo", Fields::Unnamed(2));
341    /// ```
342    pub const fn new_static(name: &'static str, fields: Fields<'static>) -> StructDef<'a> {
343        StructDef::Static { name, fields }
344    }
345
346    /// Create a new [`StructDef::Dynamic`] instance.
347    ///
348    /// This is used when the struct's fields may vary at runtime.
349    ///
350    /// # Examples
351    ///
352    /// ```
353    /// use valuable::{StructDef, Fields};
354    ///
355    /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(3));
356    /// ```
357    pub const fn new_dynamic(name: &'a str, fields: Fields<'a>) -> StructDef<'a> {
358        StructDef::Dynamic { name, fields }
359    }
360
361    /// Returns the struct's name
362    ///
363    /// # Examples
364    ///
365    /// With a static struct
366    ///
367    /// ```
368    /// use valuable::{StructDef, Fields};
369    ///
370    /// let def = StructDef::new_static("Foo", Fields::Unnamed(1));
371    /// assert_eq!("Foo", def.name());
372    /// ```
373    ///
374    /// With a dynamic struct
375    ///
376    /// ```
377    /// use valuable::{StructDef, Fields};
378    ///
379    /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(2));
380    /// assert_eq!("Foo", def.name());
381    /// ```
382    pub const fn name(&self) -> &'a str {
383        match self {
384            StructDef::Static { name, .. } => name,
385            StructDef::Dynamic { name, .. } => name,
386        }
387    }
388
389    /// Returns the struct's fields
390    ///
391    /// # Examples
392    ///
393    /// With a static struct
394    ///
395    /// ```
396    /// use valuable::{StructDef, Fields};
397    ///
398    /// let def = StructDef::new_static("Foo", Fields::Unnamed(3));
399    /// assert!(matches!(def.fields(), Fields::Unnamed(_)));
400    /// ```
401    ///
402    /// With a dynamic struct
403    ///
404    /// ```
405    /// use valuable::{StructDef, Fields};
406    ///
407    /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(1));
408    /// assert!(matches!(def.fields(), Fields::Unnamed(_)));
409    /// ```
410    pub const fn fields(&self) -> &Fields<'a> {
411        match self {
412            StructDef::Static { fields, .. } => fields,
413            StructDef::Dynamic { fields, .. } => fields,
414        }
415    }
416
417    /// Returns `true` if the struct is [statically defined](StructDef::Static).
418    ///
419    /// # Examples
420    ///
421    /// With a static struct
422    ///
423    /// ```
424    /// use valuable::{StructDef, Fields};
425    ///
426    /// let def = StructDef::new_static("Foo", Fields::Unnamed(2));
427    /// assert!(def.is_static());
428    /// ```
429    ///
430    /// With a dynamic struct
431    ///
432    /// ```
433    /// use valuable::{StructDef, Fields};
434    ///
435    /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(4));
436    /// assert!(!def.is_static());
437    /// ```
438    pub const fn is_static(&self) -> bool {
439        matches!(self, StructDef::Static { .. })
440    }
441
442    /// Returns `true` if the struct is [dynamically defined](StructDef::Dynamic).
443    ///
444    /// # Examples
445    ///
446    /// With a static struct
447    ///
448    /// ```
449    /// use valuable::{StructDef, Fields};
450    ///
451    /// let def = StructDef::new_static("Foo", Fields::Unnamed(1));
452    /// assert!(!def.is_dynamic());
453    /// ```
454    ///
455    /// With a dynamic struct
456    ///
457    /// ```
458    /// use valuable::{StructDef, Fields};
459    ///
460    /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(1));
461    /// assert!(def.is_dynamic());
462    /// ```
463    pub const fn is_dynamic(&self) -> bool {
464        matches!(self, StructDef::Dynamic { .. })
465    }
466}
467
468macro_rules! deref {
469    (
470        $(
471            $(#[$attrs:meta])*
472            $ty:ty,
473        )*
474    ) => {
475        $(
476            $(#[$attrs])*
477            impl<T: ?Sized + Structable> Structable for $ty {
478                fn definition(&self) -> StructDef<'_> {
479                    T::definition(&**self)
480                }
481            }
482        )*
483    };
484}
485
486deref! {
487    &T,
488    &mut T,
489    #[cfg(feature = "alloc")]
490    alloc::boxed::Box<T>,
491    #[cfg(feature = "alloc")]
492    alloc::rc::Rc<T>,
493    #[cfg(not(valuable_no_atomic_cas))]
494    #[cfg(feature = "alloc")]
495    alloc::sync::Arc<T>,
496}