extern crate self as enum_tree;
pub use enum_tree_derive::{DeepVariants, EnumFrom, IntoAncestors};
pub trait DeepVariants: Sized + 'static
{
const DEEP_VARIANTS: &'static [Self];
}
pub trait DeepCount
{
const DEEP_COUNT: usize;
}
impl<T: DeepVariants> DeepCount for T
{
const DEEP_COUNT: usize = Self::DEEP_VARIANTS.len();
}
#[cfg(test)]
mod tests
{
use enum_tree_derive::DeepVariants;
use super::*;
#[derive(DeepVariants, PartialEq, Eq, Debug)]
enum EmptyEnum {}
#[derive(DeepVariants, PartialEq, Eq, Debug)]
enum UnitEnum2
{
A,
B,
}
#[derive(DeepVariants, PartialEq, Eq, Debug)]
enum UnitEnum3
{
A,
B,
C,
}
#[derive(DeepVariants, PartialEq, Eq, Debug)]
enum UnitEnum5
{
A,
B,
C,
D,
E,
}
#[derive(DeepVariants, PartialEq, Eq, Debug)]
enum SingletonEnum
{
Empty(EmptyEnum),
Var2(UnitEnum2),
Var3(UnitEnum3),
Var5(UnitEnum5),
}
#[derive(DeepVariants, PartialEq, Eq, Debug)]
enum MixedEnum
{
UnitA,
Empty(EmptyEnum),
Var2(UnitEnum2),
UnitB,
Tuple(SingletonEnum),
Var5(UnitEnum5),
UnitC,
}
#[test]
fn empty_enum()
{
assert_eq!(EmptyEnum::DEEP_VARIANTS, &[]);
assert_eq!(EmptyEnum::DEEP_COUNT, 0);
}
#[test]
fn unit_enum()
{
assert_eq!(UnitEnum3::DEEP_COUNT, 3);
}
#[test]
#[expect(
clippy::identity_op,
reason = "showing the sum decomposition aids readability"
)]
fn singleton_variants()
{
assert_eq!(SingletonEnum::DEEP_COUNT, 0 + 2 + 3 + 5);
}
#[test]
#[expect(
clippy::identity_op,
reason = "showing the sum decomposition aids readability"
)]
fn mixed_variants()
{
assert_eq!(
MixedEnum::DEEP_COUNT,
1 + 0 + 2 + 1 + SingletonEnum::DEEP_COUNT + 5 + 1
);
let desired = &[
MixedEnum::UnitA,
MixedEnum::Var2(UnitEnum2::A),
MixedEnum::Var2(UnitEnum2::B),
MixedEnum::UnitB,
MixedEnum::Tuple(SingletonEnum::Var2(UnitEnum2::A)),
MixedEnum::Tuple(SingletonEnum::Var2(UnitEnum2::B)),
MixedEnum::Tuple(SingletonEnum::Var3(UnitEnum3::A)),
MixedEnum::Tuple(SingletonEnum::Var3(UnitEnum3::B)),
MixedEnum::Tuple(SingletonEnum::Var3(UnitEnum3::C)),
MixedEnum::Tuple(SingletonEnum::Var5(UnitEnum5::A)),
MixedEnum::Tuple(SingletonEnum::Var5(UnitEnum5::B)),
MixedEnum::Tuple(SingletonEnum::Var5(UnitEnum5::C)),
MixedEnum::Tuple(SingletonEnum::Var5(UnitEnum5::D)),
MixedEnum::Tuple(SingletonEnum::Var5(UnitEnum5::E)),
MixedEnum::Var5(UnitEnum5::A),
MixedEnum::Var5(UnitEnum5::B),
MixedEnum::Var5(UnitEnum5::C),
MixedEnum::Var5(UnitEnum5::D),
MixedEnum::Var5(UnitEnum5::E),
MixedEnum::UnitC,
];
assert_eq!(MixedEnum::DEEP_VARIANTS, desired);
}
#[derive(DeepVariants, PartialEq, Eq, Debug)]
#[expect(
dead_code,
reason = "variants exist solely to exercise the skip attribute"
)]
enum SkippedUnitEnum
{
A,
#[enum_tree(skip)]
B,
C,
}
#[derive(DeepVariants, PartialEq, Eq, Debug)]
#[expect(
dead_code,
reason = "variants exist solely to exercise the skip attribute"
)]
enum SkippedSingletonEnum
{
Unit,
Nested(UnitEnum3),
#[enum_tree(skip)]
Skipped(UnitEnum5),
}
#[test]
fn skip_unit_variant()
{
assert_eq!(SkippedUnitEnum::DEEP_COUNT, 2);
assert_eq!(
SkippedUnitEnum::DEEP_VARIANTS,
&[SkippedUnitEnum::A, SkippedUnitEnum::C]
);
}
#[test]
fn skip_singleton_variant()
{
assert_eq!(SkippedSingletonEnum::DEEP_COUNT, 1 + 3);
assert_eq!(
SkippedSingletonEnum::DEEP_VARIANTS,
&[
SkippedSingletonEnum::Unit,
SkippedSingletonEnum::Nested(UnitEnum3::A),
SkippedSingletonEnum::Nested(UnitEnum3::B),
SkippedSingletonEnum::Nested(UnitEnum3::C),
]
);
}
}
#[cfg(test)]
mod enum_from_tests
{
use enum_tree_derive::EnumFrom;
#[derive(EnumFrom, PartialEq, Eq, Debug)]
enum TupleSingleton
{
#[expect(dead_code, reason = "exercising the EnumFrom derive on a unit variant")]
Unit,
Int(#[enum_from(from)] i32),
Str(#[enum_from(from)] String),
}
#[test]
fn tuple_singleton_from()
{
let a: TupleSingleton = 7_i32.into();
assert_eq!(a, TupleSingleton::Int(7));
let b: TupleSingleton = String::from("hi").into();
assert_eq!(b, TupleSingleton::Str("hi".to_owned()));
}
#[derive(PartialEq, Eq, Debug)]
struct Wrapper(i32);
fn wrap(n: i32) -> Wrapper
{
Wrapper(n)
}
#[derive(EnumFrom, PartialEq, Eq, Debug)]
enum ViaFunc
{
Wrapped(#[enum_from(from = i32, via = wrap)] Wrapper),
}
#[test]
fn via_func()
{
let v: ViaFunc = 5_i32.into();
assert_eq!(v, ViaFunc::Wrapped(Wrapper(5)));
}
#[derive(EnumFrom, PartialEq, Eq, Debug)]
enum ViaClosure
{
Doubled(#[enum_from(from = i32, via = |x| x * 2)] i32),
}
#[test]
fn via_closure()
{
let v: ViaClosure = 4_i32.into();
assert_eq!(v, ViaClosure::Doubled(8));
}
#[derive(PartialEq, Eq, Debug, Default)]
struct HasDefault
{
inner: i32,
}
fn make_has_default() -> HasDefault
{
HasDefault { inner: 42 }
}
#[derive(PartialEq, Eq, Debug)]
struct Singleton;
#[derive(EnumFrom, PartialEq, Eq, Debug)]
enum MultiField
{
Named
{
#[enum_from(from)]
#[enum_from(default = || 4_i64)]
src: i64,
#[enum_from(default)]
a: HasDefault,
#[enum_from(default = make_has_default)]
b: HasDefault,
#[enum_from(from, default = "make_has_default")]
c: HasDefault,
#[enum_from(default = HasDefault { inner: 9 })]
d: HasDefault,
e: Singleton,
},
Unnamed(
#[enum_from(from)] i32,
#[enum_from(default)] HasDefault,
#[enum_from(default = HasDefault { inner: 11 })] HasDefault,
),
}
#[test]
fn multi_field_named()
{
let v: MultiField = 1_i64.into();
assert_eq!(
v,
MultiField::Named {
src: 1,
a: HasDefault::default(),
b: HasDefault { inner: 42 },
c: HasDefault { inner: 42 },
d: HasDefault { inner: 9 },
e: Singleton,
}
);
let w: MultiField = 6_i32.into();
assert_eq!(
w,
MultiField::Unnamed(6, HasDefault::default(), HasDefault { inner: 11 })
);
let x: MultiField = HasDefault { inner: 22 }.into();
assert_eq!(
x,
MultiField::Named {
src: 4,
a: HasDefault::default(),
b: HasDefault { inner: 42 },
c: HasDefault { inner: 22 },
d: HasDefault { inner: 9 },
e: Singleton,
}
);
}
#[derive(EnumFrom, PartialEq, Eq, Debug)]
enum MultiFieldUnnamed
{
Unnamed(
#[enum_from(from)] i64,
#[enum_from(default)] HasDefault,
#[enum_from(default = HasDefault { inner: 11 })] HasDefault,
),
}
#[test]
fn multi_field_unnamed()
{
let v: MultiFieldUnnamed = 3_i64.into();
assert_eq!(
v,
MultiFieldUnnamed::Unnamed(3, HasDefault::default(), HasDefault { inner: 11 },)
);
}
#[derive(EnumFrom, PartialEq, Eq, Debug)]
enum MultipleFromSameVariant
{
Both(#[enum_from(from)] i32, #[enum_from(from)] String),
}
#[test]
fn multiple_from_same_variant()
{
let a: MultipleFromSameVariant = 5_i32.into();
assert_eq!(a, MultipleFromSameVariant::Both(5, String::default()));
let b: MultipleFromSameVariant = "x".to_owned().into();
assert_eq!(
b,
MultipleFromSameVariant::Both(i32::default(), "x".to_owned())
);
}
}