use crate::field::*; use crate::*; use core::fmt; /// A struct-like [`Valuable`] sub-type. /// /// Implemented by [`Valuable`] types that have a struct-like shape. Fields may /// be named or unnamed (tuple). Values that implement `Structable` must return /// [`Value::Structable`] from their [`Valuable::as_value`] implementation. /// /// # Inspecting /// /// Inspecting fields contained by a `Structable` instance is done by visiting /// the struct. When visiting a `Structable`, either the `visit_named_fields()` /// or the `visit_unnamed_fields()` methods of `Visit` are called. Each method /// may be called multiple times per `Structable`, but the two methods are never /// mixed. /// /// ``` /// use valuable::{NamedValues, Valuable, Value, Visit}; /// /// #[derive(Valuable)] /// struct MyStruct { /// foo: u32, /// bar: u32, /// } /// /// struct PrintFields; /// /// impl Visit for PrintFields { /// fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { /// for (field, value) in named_values.iter() { /// println!("{}: {:?}", field.name(), value); /// } /// } /// /// fn visit_value(&mut self, value: Value<'_>) { /// match value { /// Value::Structable(v) => v.visit(self), /// _ => {} // do nothing for other types /// } /// } /// } /// /// let my_struct = MyStruct { /// foo: 123, /// bar: 456, /// }; /// /// valuable::visit(&my_struct, &mut PrintFields); /// ``` /// /// If the struct is **statically** defined, then all fields are known ahead of /// time and may be accessed via the [`StructDef`] instance returned by /// [`definition()`]. [`NamedField`] instances returned by [`definition()`] /// maybe used to efficiently extract specific field values. /// /// # Implementing /// /// Implementing `Structable` is usually done by adding `#[derive(Valuable)]` to /// a Rust `struct` definition. /// /// ``` /// use valuable::{Fields, Valuable, Structable, StructDef}; /// /// #[derive(Valuable)] /// struct MyStruct { /// foo: &'static str, /// } /// /// let my_struct = MyStruct { foo: "Hello" }; /// let fields = match my_struct.definition() { /// StructDef::Static { name, fields, .. } => { /// assert_eq!("MyStruct", name); /// fields /// } /// _ => unreachable!(), /// }; /// /// match fields { /// Fields::Named(named_fields) => { /// assert_eq!(1, named_fields.len()); /// assert_eq!("foo", named_fields[0].name()); /// } /// _ => unreachable!(), /// } /// ``` /// /// [`definition()`]: Structable::definition() pub trait Structable: Valuable { /// Returns the struct's definition. /// /// See [`StructDef`] documentation for more details. /// /// # Examples /// /// ``` /// use valuable::{Structable, Valuable}; /// /// #[derive(Valuable)] /// struct MyStruct { /// foo: u32, /// } /// /// let my_struct = MyStruct { /// foo: 123, /// }; /// /// assert_eq!("MyStruct", my_struct.definition().name()); fn definition(&self) -> StructDef<'_>; } /// A struct's name, fields, and other struct-level information. /// /// Returned by [`Structable::definition()`], `StructDef` provides the caller /// with information about the struct's definition. /// /// [`Structable::definition()`]: Structable::definition #[derive(Debug)] #[non_exhaustive] pub enum StructDef<'a> { /// The struct is statically-defined, all fields are known ahead of time. /// /// Most `Structable` definitions for Rust struct types will be /// `StructDef::Static`. /// /// # Examples /// /// A statically defined struct /// /// ``` /// use valuable::{Fields, Valuable, Structable, StructDef}; /// /// #[derive(Valuable)] /// struct MyStruct { /// foo: &'static str, /// } /// /// let my_struct = MyStruct { foo: "Hello" }; /// let fields = match my_struct.definition() { /// StructDef::Static { name, fields, ..} => { /// assert_eq!("MyStruct", name); /// fields /// } /// _ => unreachable!(), /// }; /// /// match fields { /// Fields::Named(named_fields) => { /// assert_eq!(1, named_fields.len()); /// assert_eq!("foo", named_fields[0].name()); /// } /// _ => unreachable!(), /// } /// ``` #[non_exhaustive] Static { /// The struct's name. name: &'static str, /// The struct's fields. fields: Fields<'static>, }, /// The struct is dynamically-defined, not all fields are known ahead of /// time. /// /// A dynamically-defined struct **could** be represented using /// [`Mappable`], though, using `Structable` offers benefits in a couple of /// cases. For example, when serializing a `Value`, some formats will /// serialize maps and structs differently. In this case, differentiating /// the two is required. There also are times when **some** struct fields /// are known statically, but not all of them (see second example). /// /// # Examples /// /// The struct stores field values in a `HashMap`. /// /// ``` /// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit}; /// use std::collections::HashMap; /// /// /// A dynamic struct /// struct Dyn { /// // The struct name /// name: String, /// /// // Named values. /// values: HashMap>, /// } /// /// impl Valuable for Dyn { /// fn as_value(&self) -> Value<'_> { /// Value::Structable(self) /// } /// /// fn visit(&self, visit: &mut dyn Visit) { /// // This could be optimized to batch some. /// for (field, value) in self.values.iter() { /// visit.visit_named_fields(&NamedValues::new( /// &[NamedField::new(field)], /// &[value.as_value()], /// )); /// } /// } /// } /// /// impl Structable for Dyn { /// fn definition(&self) -> StructDef<'_> { /// StructDef::new_dynamic(&self.name, Fields::Named(&[])) /// } /// } /// ``` /// /// Some fields are known statically. /// /// ``` /// use valuable::{Fields, NamedField, NamedValues, Structable, StructDef, Value, Valuable, Visit}; /// use std::collections::HashMap; /// /// struct HalfStatic { /// foo: u32, /// bar: u32, /// extra_values: HashMap>, /// } /// /// impl Valuable for HalfStatic { /// fn as_value(&self) -> Value<'_> { /// Value::Structable(self) /// } /// /// fn visit(&self, visit: &mut dyn Visit) { /// // First, visit static fields /// visit.visit_named_fields(&NamedValues::new( /// FIELDS, /// &[self.foo.as_value(), self.bar.as_value()], /// )); /// /// // This could be optimized to batch some. /// for (field, value) in self.extra_values.iter() { /// visit.visit_named_fields(&NamedValues::new( /// &[NamedField::new(field)], /// &[value.as_value()], /// )); /// } /// } /// } /// /// static FIELDS: &[NamedField<'static>] = &[ /// NamedField::new("foo"), /// NamedField::new("bar"), /// ]; /// /// impl Structable for HalfStatic { /// fn definition(&self) -> StructDef<'_> { /// // Include known fields. /// StructDef::new_dynamic( /// "HalfStatic", /// Fields::Named(FIELDS)) /// } /// } /// ``` #[non_exhaustive] Dynamic { /// The struct's name name: &'a str, /// The struct's fields. fields: Fields<'a>, }, } impl fmt::Debug for dyn Structable + '_ { fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { let def = self.definition(); if def.fields().is_named() { struct DebugStruct<'a, 'b> { fmt: fmt::DebugStruct<'a, 'b>, } let mut debug = DebugStruct { fmt: fmt.debug_struct(def.name()), }; impl Visit for DebugStruct<'_, '_> { fn visit_named_fields(&mut self, named_values: &NamedValues<'_>) { for (field, value) in named_values { self.fmt.field(field.name(), value); } } fn visit_value(&mut self, _: Value<'_>) { unreachable!() } } self.visit(&mut debug); debug.fmt.finish() } else { struct DebugStruct<'a, 'b> { fmt: fmt::DebugTuple<'a, 'b>, } let mut debug = DebugStruct { fmt: fmt.debug_tuple(def.name()), }; impl Visit for DebugStruct<'_, '_> { fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) { for value in values { self.fmt.field(value); } } fn visit_value(&mut self, _: Value<'_>) { unreachable!(); } } self.visit(&mut debug); debug.fmt.finish() } } } impl<'a> StructDef<'a> { /// Create a new [`StructDef::Static`] instance. /// /// This should be used when a struct's fields are fixed and known ahead of time. /// /// # Examples /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_static("Foo", Fields::Unnamed(2)); /// ``` pub const fn new_static(name: &'static str, fields: Fields<'static>) -> StructDef<'a> { StructDef::Static { name, fields } } /// Create a new [`StructDef::Dynamic`] instance. /// /// This is used when the struct's fields may vary at runtime. /// /// # Examples /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(3)); /// ``` pub const fn new_dynamic(name: &'a str, fields: Fields<'a>) -> StructDef<'a> { StructDef::Dynamic { name, fields } } /// Returns the struct's name /// /// # Examples /// /// With a static struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_static("Foo", Fields::Unnamed(1)); /// assert_eq!("Foo", def.name()); /// ``` /// /// With a dynamic struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(2)); /// assert_eq!("Foo", def.name()); /// ``` pub const fn name(&self) -> &'a str { match self { StructDef::Static { name, .. } => name, StructDef::Dynamic { name, .. } => name, } } /// Returns the struct's fields /// /// # Examples /// /// With a static struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_static("Foo", Fields::Unnamed(3)); /// assert!(matches!(def.fields(), Fields::Unnamed(_))); /// ``` /// /// With a dynamic struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(1)); /// assert!(matches!(def.fields(), Fields::Unnamed(_))); /// ``` pub const fn fields(&self) -> &Fields<'a> { match self { StructDef::Static { fields, .. } => fields, StructDef::Dynamic { fields, .. } => fields, } } /// Returns `true` if the struct is [statically defined](StructDef::Static). /// /// # Examples /// /// With a static struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_static("Foo", Fields::Unnamed(2)); /// assert!(def.is_static()); /// ``` /// /// With a dynamic struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(4)); /// assert!(!def.is_static()); /// ``` pub const fn is_static(&self) -> bool { matches!(self, StructDef::Static { .. }) } /// Returns `true` if the struct is [dynamically defined](StructDef::Dynamic). /// /// # Examples /// /// With a static struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_static("Foo", Fields::Unnamed(1)); /// assert!(!def.is_dynamic()); /// ``` /// /// With a dynamic struct /// /// ``` /// use valuable::{StructDef, Fields}; /// /// let def = StructDef::new_dynamic("Foo", Fields::Unnamed(1)); /// assert!(def.is_dynamic()); /// ``` pub const fn is_dynamic(&self) -> bool { matches!(self, StructDef::Dynamic { .. }) } } macro_rules! deref { ( $( $(#[$attrs:meta])* $ty:ty, )* ) => { $( $(#[$attrs])* impl Structable for $ty { fn definition(&self) -> StructDef<'_> { T::definition(&**self) } } )* }; } 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, }