| 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<String, Box<dyn Valuable>>, |
| /// } |
| /// |
| /// 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<String, Box<dyn Valuable>>, |
| /// } |
| /// |
| /// 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<T: ?Sized + Structable> Structable for $ty { |
| fn definition(&self) -> StructDef<'_> { |
| T::definition(&**self) |
| } |
| } |
| )* |
| }; |
| } |
| |
| 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>, |
| } |