682 lines
17 KiB
Rust
682 lines
17 KiB
Rust
use crate::field::*;
|
|
use crate::*;
|
|
|
|
#[cfg(feature = "alloc")]
|
|
use alloc::format;
|
|
use core::fmt;
|
|
|
|
/// An enum-like [`Valuable`] sub-type.
|
|
///
|
|
/// Implemented by [`Valuable`] types that have an enum-like shape. Fields may
|
|
/// be named or unnamed (tuple). Values that implement `Enumerable` must return
|
|
/// [`Value::Enumerable`] from their [`Valuable::as_value`] implementation.
|
|
///
|
|
/// # Inspecting
|
|
///
|
|
/// The [`variant()`] method returns the `Enumerable` instance's variant. The
|
|
/// `Enumerable` may also have unnamed fields (tuple) or named fields.
|
|
/// Inspecting the field values is done by visiting the enum. When visiting an
|
|
/// `Enumerable`, either the [`visit_named_fields()`] or the
|
|
/// [`visit_unnamed_fields()`] methods of [`Visit`] are called. Each method may
|
|
/// be called multiple times per `Enumerable`, but the two methods are never
|
|
/// mixed.
|
|
///
|
|
/// [`variant()`]: Enumerable::variant
|
|
/// [`visit_named_fields()`]: Visit::visit_named_fields
|
|
/// [`visit_unnamed_fields()`]: Visit::visit_unnamed_fields
|
|
///
|
|
/// ```
|
|
/// use valuable::{Valuable, Value, Visit};
|
|
///
|
|
/// #[derive(Valuable)]
|
|
/// enum MyEnum {
|
|
/// Foo,
|
|
/// Bar(u32),
|
|
/// }
|
|
///
|
|
/// struct PrintVariant;
|
|
///
|
|
/// impl Visit for PrintVariant {
|
|
/// fn visit_unnamed_fields(&mut self, values: &[Value<'_>]) {
|
|
/// for value in values {
|
|
/// println!(" - {:?}", value);
|
|
/// }
|
|
/// }
|
|
///
|
|
/// fn visit_value(&mut self, value: Value<'_>) {
|
|
/// match value {
|
|
/// Value::Enumerable(v) => {
|
|
/// println!("{}", v.variant().name());
|
|
/// v.visit(self)
|
|
/// }
|
|
/// _ => {}
|
|
/// }
|
|
/// }
|
|
/// }
|
|
///
|
|
/// let my_enum = MyEnum::Bar(123);
|
|
///
|
|
/// valuable::visit(&my_enum, &mut PrintVariant);
|
|
/// ```
|
|
///
|
|
/// If the enum is **statically** defined, then all variants, and variant fields
|
|
/// are known ahead of time and may be accessed via the [`EnumDef`] instance
|
|
/// returned by [`definition()`].
|
|
///
|
|
/// [`definition()`]: Enumerable::definition
|
|
///
|
|
/// # Implementing
|
|
///
|
|
/// Implementing `Enumerable` is usually done by adding `#[derive(Valuable)]` to
|
|
/// a Rust `enum` definition.
|
|
///
|
|
/// ```
|
|
/// use valuable::{Valuable, Enumerable, EnumDef};
|
|
///
|
|
/// #[derive(Valuable)]
|
|
/// enum MyEnum {
|
|
/// Foo,
|
|
/// Bar(u32),
|
|
/// }
|
|
///
|
|
/// let my_enum = MyEnum::Bar(123);
|
|
///
|
|
/// let variants = match my_enum.definition() {
|
|
/// EnumDef::Static { name, variants, .. } => {
|
|
/// assert_eq!("MyEnum", name);
|
|
/// variants
|
|
/// }
|
|
/// _ => unreachable!(),
|
|
/// };
|
|
///
|
|
/// assert_eq!(2, variants.len());
|
|
/// assert_eq!("Foo", variants[0].name());
|
|
/// assert!(variants[0].fields().is_unnamed());
|
|
/// ```
|
|
pub trait Enumerable: Valuable {
|
|
/// Returns the enum's definition.
|
|
///
|
|
/// See [`EnumDef`] documentation for more details.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use valuable::{Enumerable, Valuable};
|
|
///
|
|
/// #[derive(Valuable)]
|
|
/// enum MyEnum {
|
|
/// Foo,
|
|
/// Bar(u32),
|
|
/// }
|
|
///
|
|
/// let my_enum = MyEnum::Bar(123);
|
|
///
|
|
/// assert_eq!("MyEnum", my_enum.definition().name());
|
|
/// ```
|
|
fn definition(&self) -> EnumDef<'_>;
|
|
|
|
/// Returns the `enum`'s current variant.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use valuable::{Enumerable, Valuable};
|
|
///
|
|
/// #[derive(Valuable)]
|
|
/// enum MyEnum {
|
|
/// Foo,
|
|
/// Bar(u32),
|
|
/// }
|
|
///
|
|
/// let my_enum = MyEnum::Foo;
|
|
/// assert_eq!("Foo", my_enum.variant().name());
|
|
/// ```
|
|
fn variant(&self) -> Variant<'_>;
|
|
}
|
|
|
|
/// An enum's variants, variant fields, and other enum-level information.
|
|
///
|
|
/// Returned by [`Enumerable::definition()`], `EnumDef` provides the caller with
|
|
/// information about the enum's definition.
|
|
#[non_exhaustive]
|
|
#[derive(Debug)]
|
|
pub enum EnumDef<'a> {
|
|
/// The enum is statically-defined, all variants and variant-level fields
|
|
/// are known ahead of time.
|
|
///
|
|
/// Most `Enumerable` definitions for Rust enum types will be
|
|
/// `EnumDef::Static`.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// A statically defined enum
|
|
///
|
|
/// ```
|
|
/// use valuable::{Valuable, Enumerable, EnumDef};
|
|
///
|
|
/// #[derive(Valuable)]
|
|
/// enum MyEnum {
|
|
/// Foo,
|
|
/// Bar(u32),
|
|
/// }
|
|
///
|
|
/// let my_enum = MyEnum::Bar(123);
|
|
///
|
|
/// let variants = match my_enum.definition() {
|
|
/// EnumDef::Static { name, variants, .. } => {
|
|
/// assert_eq!("MyEnum", name);
|
|
/// variants
|
|
/// }
|
|
/// _ => unreachable!(),
|
|
/// };
|
|
///
|
|
/// assert_eq!(2, variants.len());
|
|
/// assert_eq!("Foo", variants[0].name());
|
|
/// assert_eq!("Bar", variants[1].name());
|
|
/// ```
|
|
#[non_exhaustive]
|
|
Static {
|
|
/// The enum's name
|
|
name: &'static str,
|
|
|
|
/// The enum's variants
|
|
variants: &'static [VariantDef<'static>],
|
|
},
|
|
|
|
/// The enum is dynamically-defined, not all variants and fields are known
|
|
/// ahead of time.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// The enum variant is tracked as a string
|
|
///
|
|
/// ```
|
|
/// use valuable::{Enumerable, EnumDef, Fields, VariantDef, Valuable, Value, Variant, Visit};
|
|
///
|
|
/// /// A dynamic enum
|
|
/// struct DynEnum {
|
|
/// // The enum name
|
|
/// name: String,
|
|
///
|
|
/// // The current variant
|
|
/// variant: String,
|
|
/// }
|
|
///
|
|
/// impl Valuable for DynEnum {
|
|
/// fn as_value(&self) -> Value<'_> {
|
|
/// Value::Enumerable(self)
|
|
/// }
|
|
///
|
|
/// fn visit(&self, _visit: &mut dyn Visit) {
|
|
/// // No variant fields, so there is nothing to call here.
|
|
/// }
|
|
/// }
|
|
///
|
|
/// impl Enumerable for DynEnum {
|
|
/// fn definition(&self) -> EnumDef<'_> {
|
|
/// EnumDef::new_dynamic(&self.name, &[])
|
|
/// }
|
|
///
|
|
/// fn variant(&self) -> Variant<'_> {
|
|
/// Variant::Dynamic(VariantDef::new(&self.variant, Fields::Unnamed(0)))
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
#[non_exhaustive]
|
|
Dynamic {
|
|
/// The enum's name
|
|
name: &'a str,
|
|
|
|
/// The enum's variants
|
|
variants: &'a [VariantDef<'a>],
|
|
},
|
|
}
|
|
|
|
/// An enum variant definition.
|
|
///
|
|
/// Included with [`EnumDef`] returned by [`Enumerable::definition()`],
|
|
/// `VariantDef` provides the caller with information about a specific variant.
|
|
#[derive(Debug)]
|
|
pub struct VariantDef<'a> {
|
|
/// Variant name
|
|
name: &'a str,
|
|
|
|
/// Variant fields
|
|
fields: Fields<'a>,
|
|
}
|
|
|
|
/// An enum variant
|
|
///
|
|
/// Returned by [`Enumerable::variant()`], `Variant` represents a single enum
|
|
/// variant.
|
|
#[derive(Debug)]
|
|
pub enum Variant<'a> {
|
|
/// The variant is statically defined by the associated enum.
|
|
Static(&'static VariantDef<'static>),
|
|
|
|
/// The variant is dynamically defined and not included as part of
|
|
/// [`Enumerable::definition()`].
|
|
Dynamic(VariantDef<'a>),
|
|
}
|
|
|
|
impl<'a> EnumDef<'a> {
|
|
/// Create a new [`EnumDef::Static`] instance.
|
|
///
|
|
/// This should be used when an enum's variants are fixed and known ahead of
|
|
/// time.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use valuable::{EnumDef, Fields, VariantDef};
|
|
///
|
|
/// static VARIANTS: &[VariantDef<'static>] = &[
|
|
/// VariantDef::new("Bar", Fields::Unnamed(1)),
|
|
/// ];
|
|
///
|
|
/// let def = EnumDef::new_static( "Foo", VARIANTS);
|
|
/// ```
|
|
pub const fn new_static(
|
|
name: &'static str,
|
|
variants: &'static [VariantDef<'static>],
|
|
) -> EnumDef<'a> {
|
|
EnumDef::Static { name, variants }
|
|
}
|
|
|
|
/// Create a new [`EnumDef::Dynamic`] instance.
|
|
///
|
|
/// This is used when the enum's variants may vary at runtime.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use valuable::{EnumDef, Fields, VariantDef};
|
|
///
|
|
/// let def = EnumDef::new_dynamic(
|
|
/// "Foo",
|
|
/// &[VariantDef::new("Bar", Fields::Unnamed(1))]
|
|
/// );
|
|
/// ```
|
|
pub const fn new_dynamic(name: &'a str, variants: &'a [VariantDef<'a>]) -> EnumDef<'a> {
|
|
EnumDef::Dynamic { name, variants }
|
|
}
|
|
|
|
/// Returns the enum's name
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use valuable::{Enumerable, Valuable};
|
|
///
|
|
/// #[derive(Valuable)]
|
|
/// enum Foo {
|
|
/// Bar,
|
|
/// Baz,
|
|
/// }
|
|
///
|
|
/// let def = Foo::Bar.definition();
|
|
/// assert_eq!("Foo", def.name());
|
|
/// ```
|
|
pub fn name(&self) -> &str {
|
|
match self {
|
|
EnumDef::Static { name, .. } => name,
|
|
EnumDef::Dynamic { name, .. } => name,
|
|
}
|
|
}
|
|
|
|
/// Returns the enum's variants
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use valuable::{Enumerable, Valuable};
|
|
///
|
|
/// #[derive(Valuable)]
|
|
/// enum Foo {
|
|
/// Bar,
|
|
/// Baz,
|
|
/// }
|
|
///
|
|
/// let def = Foo::Bar.definition();
|
|
/// let variants = def.variants();
|
|
///
|
|
/// assert_eq!(2, variants.len());
|
|
/// assert_eq!("Bar", variants[0].name());
|
|
/// ```
|
|
pub fn variants(&self) -> &[VariantDef<'_>] {
|
|
match self {
|
|
EnumDef::Static { variants, .. } => variants,
|
|
EnumDef::Dynamic { variants, .. } => variants,
|
|
}
|
|
}
|
|
|
|
/// Returns `true` if the enum is [statically defined](EnumDef::Static).
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// With a static enum
|
|
///
|
|
/// ```
|
|
/// use valuable::{Enumerable, Valuable};
|
|
///
|
|
/// #[derive(Valuable)]
|
|
/// enum Foo {
|
|
/// Bar,
|
|
/// Baz,
|
|
/// }
|
|
///
|
|
/// let def = Foo::Bar.definition();
|
|
/// assert!(def.is_static());
|
|
/// ```
|
|
///
|
|
/// With a dynamic enum
|
|
///
|
|
/// ```
|
|
/// use valuable::{EnumDef, Fields, VariantDef};
|
|
///
|
|
/// let def = EnumDef::new_dynamic("Foo", &[]);
|
|
/// assert!(!def.is_static());
|
|
/// ```
|
|
pub fn is_static(&self) -> bool {
|
|
matches!(self, EnumDef::Static { .. })
|
|
}
|
|
|
|
/// Returns `true` if the enum is [dynamically defined](EnumDef::Dynamic).
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// With a static enum
|
|
///
|
|
/// ```
|
|
/// use valuable::{Enumerable, Valuable};
|
|
///
|
|
/// #[derive(Valuable)]
|
|
/// enum Foo {
|
|
/// Bar,
|
|
/// Baz,
|
|
/// }
|
|
///
|
|
/// let def = Foo::Bar.definition();
|
|
/// assert!(!def.is_dynamic());
|
|
/// ```
|
|
///
|
|
/// With a dynamic enum
|
|
///
|
|
/// ```
|
|
/// use valuable::{EnumDef, Fields, VariantDef};
|
|
///
|
|
/// let def = EnumDef::new_dynamic("Foo", &[]);
|
|
/// assert!(def.is_dynamic());
|
|
/// ```
|
|
pub fn is_dynamic(&self) -> bool {
|
|
matches!(self, EnumDef::Dynamic { .. })
|
|
}
|
|
}
|
|
|
|
impl<'a> VariantDef<'a> {
|
|
/// Creates a new `VariantDef` instance.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use valuable::{Fields, VariantDef};
|
|
///
|
|
/// let def = VariantDef::new("Foo", Fields::Unnamed(2));
|
|
/// ```
|
|
pub const fn new(name: &'a str, fields: Fields<'a>) -> VariantDef<'a> {
|
|
VariantDef { name, fields }
|
|
}
|
|
|
|
/// Returns the variant's name
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use valuable::{Fields, VariantDef};
|
|
///
|
|
/// let def = VariantDef::new("Foo", Fields::Unnamed(2));
|
|
/// assert_eq!("Foo", def.name());
|
|
/// ```
|
|
pub fn name(&self) -> &str {
|
|
self.name
|
|
}
|
|
|
|
/// Returns the variant's fields
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use valuable::{Fields, VariantDef};
|
|
///
|
|
/// let def = VariantDef::new("Foo", Fields::Unnamed(3));
|
|
/// assert!(matches!(def.fields(), Fields::Unnamed(_)));
|
|
/// ```
|
|
pub fn fields(&self) -> &Fields<'_> {
|
|
&self.fields
|
|
}
|
|
}
|
|
|
|
impl Variant<'_> {
|
|
/// Returns the variant's name
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use valuable::{Fields, Variant, VariantDef};
|
|
///
|
|
/// static VARIANT: &VariantDef<'static> = &VariantDef::new(
|
|
/// "Foo", Fields::Unnamed(2));
|
|
///
|
|
/// let variant = Variant::Static(VARIANT);
|
|
/// assert_eq!("Foo", variant.name());
|
|
/// ```
|
|
pub fn name(&self) -> &str {
|
|
match self {
|
|
Variant::Static(v) => v.name(),
|
|
Variant::Dynamic(v) => v.name(),
|
|
}
|
|
}
|
|
|
|
/// Returns the variant's fields
|
|
pub fn fields(&self) -> &Fields<'_> {
|
|
match self {
|
|
Variant::Static(v) => v.fields(),
|
|
Variant::Dynamic(v) => v.fields(),
|
|
}
|
|
}
|
|
|
|
/// Returns `true` if the variant has associated named fields.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// With named fields
|
|
///
|
|
/// ```
|
|
/// use valuable::{Fields, NamedField, Variant, VariantDef};
|
|
///
|
|
/// static VARIANT: &VariantDef<'static> = &VariantDef::new(
|
|
/// "Foo", Fields::Named(&[NamedField::new("hello")]));
|
|
///
|
|
/// let variant = Variant::Static(VARIANT);
|
|
/// assert!(variant.is_named_fields());
|
|
/// ```
|
|
///
|
|
/// With unnamed fields
|
|
///
|
|
/// ```
|
|
/// use valuable::{Fields, Variant, VariantDef};
|
|
///
|
|
/// static VARIANT: &VariantDef<'static> = &VariantDef::new(
|
|
/// "Foo", Fields::Unnamed(1));
|
|
///
|
|
/// let variant = Variant::Static(VARIANT);
|
|
/// assert!(!variant.is_named_fields());
|
|
/// ```
|
|
pub fn is_named_fields(&self) -> bool {
|
|
self.fields().is_named()
|
|
}
|
|
|
|
/// Returns `true` if the variant has associated unnamed fields.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// With named fields
|
|
///
|
|
/// ```
|
|
/// use valuable::{Fields, NamedField, Variant, VariantDef};
|
|
///
|
|
/// static VARIANT: &VariantDef<'static> = &VariantDef::new(
|
|
/// "Foo", Fields::Named(&[NamedField::new("hello")]));
|
|
///
|
|
/// let variant = Variant::Static(VARIANT);
|
|
/// assert!(!variant.is_unnamed_fields());
|
|
/// ```
|
|
///
|
|
/// With unnamed fields
|
|
///
|
|
/// ```
|
|
/// use valuable::{Fields, Variant, VariantDef};
|
|
///
|
|
/// static VARIANT: &VariantDef<'static> = &VariantDef::new(
|
|
/// "Foo", Fields::Unnamed(1));
|
|
///
|
|
/// let variant = Variant::Static(VARIANT);
|
|
/// assert!(variant.is_unnamed_fields());
|
|
/// ```
|
|
pub fn is_unnamed_fields(&self) -> bool {
|
|
!self.is_named_fields()
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for dyn Enumerable + '_ {
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
let variant = self.variant();
|
|
#[cfg(feature = "alloc")]
|
|
let name = format!("{}::{}", self.definition().name(), variant.name());
|
|
#[cfg(not(feature = "alloc"))]
|
|
let name = variant.name();
|
|
|
|
if variant.is_named_fields() {
|
|
struct DebugEnum<'a, 'b> {
|
|
fmt: fmt::DebugStruct<'a, 'b>,
|
|
}
|
|
|
|
let mut debug = DebugEnum {
|
|
fmt: fmt.debug_struct(&name),
|
|
};
|
|
|
|
impl Visit for DebugEnum<'_, '_> {
|
|
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 DebugEnum<'a, 'b> {
|
|
fmt: fmt::DebugTuple<'a, 'b>,
|
|
}
|
|
|
|
let mut debug = DebugEnum {
|
|
fmt: fmt.debug_tuple(&name),
|
|
};
|
|
|
|
impl Visit for DebugEnum<'_, '_> {
|
|
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()
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! deref {
|
|
(
|
|
$(
|
|
$(#[$attrs:meta])*
|
|
$ty:ty,
|
|
)*
|
|
) => {
|
|
$(
|
|
$(#[$attrs])*
|
|
impl<T: ?Sized + Enumerable> Enumerable for $ty {
|
|
fn definition(&self) -> EnumDef<'_> {
|
|
T::definition(&**self)
|
|
}
|
|
|
|
fn variant(&self) -> Variant<'_> {
|
|
T::variant(&**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>,
|
|
}
|
|
|
|
static RESULT_VARIANTS: &[VariantDef<'static>] = &[
|
|
VariantDef::new("Ok", Fields::Unnamed(1)),
|
|
VariantDef::new("Err", Fields::Unnamed(1)),
|
|
];
|
|
|
|
impl<T, E> Enumerable for Result<T, E>
|
|
where
|
|
T: Valuable,
|
|
E: Valuable,
|
|
{
|
|
fn definition(&self) -> EnumDef<'_> {
|
|
EnumDef::new_static("Result", RESULT_VARIANTS)
|
|
}
|
|
|
|
fn variant(&self) -> Variant<'_> {
|
|
match self {
|
|
Ok(_) => Variant::Static(&RESULT_VARIANTS[0]),
|
|
Err(_) => Variant::Static(&RESULT_VARIANTS[1]),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T, E> Valuable for Result<T, E>
|
|
where
|
|
T: Valuable,
|
|
E: Valuable,
|
|
{
|
|
fn as_value(&self) -> Value<'_> {
|
|
Value::Enumerable(self)
|
|
}
|
|
|
|
fn visit(&self, visitor: &mut dyn Visit) {
|
|
match self {
|
|
Ok(val) => visitor.visit_unnamed_fields(&[val.as_value()]),
|
|
Err(val) => visitor.visit_unnamed_fields(&[val.as_value()]),
|
|
}
|
|
}
|
|
}
|