use crate::field::*;
use crate::*;
#[cfg(feature = "alloc")]
use alloc::format;
use core::fmt;
pub trait Enumerable: Valuable {
fn definition(&self) -> EnumDef<'_>;
fn variant(&self) -> Variant<'_>;
}
#[non_exhaustive]
#[derive(Debug)]
pub enum EnumDef<'a> {
#[non_exhaustive]
Static {
name: &'static str,
variants: &'static [VariantDef<'static>],
},
#[non_exhaustive]
Dynamic {
name: &'a str,
variants: &'a [VariantDef<'a>],
},
}
#[derive(Debug)]
pub struct VariantDef<'a> {
name: &'a str,
fields: Fields<'a>,
}
#[derive(Debug)]
pub enum Variant<'a> {
Static(&'static VariantDef<'static>),
Dynamic(VariantDef<'a>),
}
impl<'a> EnumDef<'a> {
pub const fn new_static(
name: &'static str,
variants: &'static [VariantDef<'static>],
) -> EnumDef<'a> {
EnumDef::Static { name, variants }
}
pub const fn new_dynamic(name: &'a str, variants: &'a [VariantDef<'a>]) -> EnumDef<'a> {
EnumDef::Dynamic { name, variants }
}
pub fn name(&self) -> &str {
match self {
EnumDef::Static { name, .. } => name,
EnumDef::Dynamic { name, .. } => name,
}
}
pub fn variants(&self) -> &[VariantDef<'_>] {
match self {
EnumDef::Static { variants, .. } => variants,
EnumDef::Dynamic { variants, .. } => variants,
}
}
pub fn is_static(&self) -> bool {
matches!(self, EnumDef::Static { .. })
}
pub fn is_dynamic(&self) -> bool {
matches!(self, EnumDef::Dynamic { .. })
}
}
impl<'a> VariantDef<'a> {
pub const fn new(name: &'a str, fields: Fields<'a>) -> VariantDef<'a> {
VariantDef { name, fields }
}
pub fn name(&self) -> &str {
self.name
}
pub fn fields(&self) -> &Fields<'_> {
&self.fields
}
}
impl Variant<'_> {
pub fn name(&self) -> &str {
match self {
Variant::Static(v) => v.name(),
Variant::Dynamic(v) => v.name(),
}
}
pub fn fields(&self) -> &Fields<'_> {
match self {
Variant::Static(v) => v.fields(),
Variant::Dynamic(v) => v.fields(),
}
}
pub fn is_named_fields(&self) -> bool {
self.fields().is_named()
}
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()]),
}
}
}