Skip to main content

enum_tree/
lib.rs

1extern crate self as enum_tree;
2
3pub use enum_tree_derive::{DeepVariants, EnumFrom, IntoAncestors};
4
5/// Types that expose every reachable value as a compile-time constant slice.
6///
7/// The companion [`DeepVariants`](enum_tree_derive::DeepVariants)
8/// derive macro implements this trait for an enum whose variants are
9/// either unit variants or singleton tuple variants over types that also
10/// implement `DeepVariants`.
11///
12/// # Example
13///
14/// ```
15/// use enum_tree::DeepVariants;
16///
17/// #[derive(DeepVariants, PartialEq, Eq, Debug)]
18/// enum Light
19/// {
20///     Red,
21///     Yellow,
22///     Green,
23/// }
24///
25/// assert_eq!(
26///     Light::DEEP_VARIANTS,
27///     &[Light::Red, Light::Yellow, Light::Green]
28/// );
29/// ```
30pub trait DeepVariants: Sized + 'static
31{
32    /// The flat list of every reachable variant value.
33    const DEEP_VARIANTS: &'static [Self];
34}
35
36/// Convenience companion trait that exposes the length of
37/// [`DeepVariants::DEEP_VARIANTS`] without dereferencing the slice.
38///
39/// This is a blanket impl over every `T: DeepVariants`, so callers can write
40/// `Foo::DEEP_COUNT` for any type that implements `DeepVariants`. There is
41/// nothing to derive or implement manually.
42pub trait DeepCount
43{
44    /// The number of values in [`DeepVariants::DEEP_VARIANTS`].
45    const DEEP_COUNT: usize;
46}
47impl<T: DeepVariants> DeepCount for T
48{
49    const DEEP_COUNT: usize = Self::DEEP_VARIANTS.len();
50}
51
52#[cfg(test)]
53mod tests
54{
55    use enum_tree_derive::DeepVariants;
56
57    use super::*;
58
59    #[derive(DeepVariants, PartialEq, Eq, Debug)]
60    enum EmptyEnum {}
61
62    #[derive(DeepVariants, PartialEq, Eq, Debug)]
63    enum UnitEnum2
64    {
65        A,
66        B,
67    }
68
69    #[derive(DeepVariants, PartialEq, Eq, Debug)]
70    enum UnitEnum3
71    {
72        A,
73        B,
74        C,
75    }
76
77    #[derive(DeepVariants, PartialEq, Eq, Debug)]
78    enum UnitEnum5
79    {
80        A,
81        B,
82        C,
83        D,
84        E,
85    }
86
87    #[derive(DeepVariants, PartialEq, Eq, Debug)]
88    enum SingletonEnum
89    {
90        Empty(EmptyEnum),
91        Var2(UnitEnum2),
92        Var3(UnitEnum3),
93        Var5(UnitEnum5),
94    }
95
96    // #[derive(DeepVariants, PartialEq, Eq, Debug)]
97    // enum TupleEnum
98    // {
99    //     Single(UnitEnum2),
100    //     Pair(UnitEnum3, UnitEnum5),
101    // }
102
103    #[derive(DeepVariants, PartialEq, Eq, Debug)]
104    enum MixedEnum
105    {
106        UnitA,
107        Empty(EmptyEnum),
108        Var2(UnitEnum2),
109        UnitB,
110        Tuple(SingletonEnum),
111        Var5(UnitEnum5),
112        UnitC,
113    }
114
115    #[test]
116    fn empty_enum()
117    {
118        assert_eq!(EmptyEnum::DEEP_VARIANTS, &[]);
119        assert_eq!(EmptyEnum::DEEP_COUNT, 0);
120    }
121
122    #[test]
123    fn unit_enum()
124    {
125        assert_eq!(UnitEnum3::DEEP_COUNT, 3);
126    }
127
128    #[test]
129    #[expect(
130        clippy::identity_op,
131        reason = "showing the sum decomposition aids readability"
132    )]
133    fn singleton_variants()
134    {
135        assert_eq!(SingletonEnum::DEEP_COUNT, 0 + 2 + 3 + 5);
136    }
137
138    #[test]
139    #[expect(
140        clippy::identity_op,
141        reason = "showing the sum decomposition aids readability"
142    )]
143    fn mixed_variants()
144    {
145        assert_eq!(
146            MixedEnum::DEEP_COUNT,
147            1 + 0 + 2 + 1 + SingletonEnum::DEEP_COUNT + 5 + 1
148        );
149
150        let desired = &[
151            MixedEnum::UnitA,
152            MixedEnum::Var2(UnitEnum2::A),
153            MixedEnum::Var2(UnitEnum2::B),
154            MixedEnum::UnitB,
155            MixedEnum::Tuple(SingletonEnum::Var2(UnitEnum2::A)),
156            MixedEnum::Tuple(SingletonEnum::Var2(UnitEnum2::B)),
157            MixedEnum::Tuple(SingletonEnum::Var3(UnitEnum3::A)),
158            MixedEnum::Tuple(SingletonEnum::Var3(UnitEnum3::B)),
159            MixedEnum::Tuple(SingletonEnum::Var3(UnitEnum3::C)),
160            MixedEnum::Tuple(SingletonEnum::Var5(UnitEnum5::A)),
161            MixedEnum::Tuple(SingletonEnum::Var5(UnitEnum5::B)),
162            MixedEnum::Tuple(SingletonEnum::Var5(UnitEnum5::C)),
163            MixedEnum::Tuple(SingletonEnum::Var5(UnitEnum5::D)),
164            MixedEnum::Tuple(SingletonEnum::Var5(UnitEnum5::E)),
165            MixedEnum::Var5(UnitEnum5::A),
166            MixedEnum::Var5(UnitEnum5::B),
167            MixedEnum::Var5(UnitEnum5::C),
168            MixedEnum::Var5(UnitEnum5::D),
169            MixedEnum::Var5(UnitEnum5::E),
170            MixedEnum::UnitC,
171        ];
172        assert_eq!(MixedEnum::DEEP_VARIANTS, desired);
173    }
174
175    #[derive(DeepVariants, PartialEq, Eq, Debug)]
176    #[expect(
177        dead_code,
178        reason = "variants exist solely to exercise the skip attribute"
179    )]
180    enum SkippedUnitEnum
181    {
182        A,
183        #[enum_tree(skip)]
184        B,
185        C,
186    }
187
188    #[derive(DeepVariants, PartialEq, Eq, Debug)]
189    #[expect(
190        dead_code,
191        reason = "variants exist solely to exercise the skip attribute"
192    )]
193    enum SkippedSingletonEnum
194    {
195        Unit,
196        Nested(UnitEnum3),
197        #[enum_tree(skip)]
198        Skipped(UnitEnum5),
199    }
200
201    #[test]
202    fn skip_unit_variant()
203    {
204        assert_eq!(SkippedUnitEnum::DEEP_COUNT, 2);
205        assert_eq!(
206            SkippedUnitEnum::DEEP_VARIANTS,
207            &[SkippedUnitEnum::A, SkippedUnitEnum::C]
208        );
209    }
210
211    #[test]
212    fn skip_singleton_variant()
213    {
214        assert_eq!(SkippedSingletonEnum::DEEP_COUNT, 1 + 3);
215        assert_eq!(
216            SkippedSingletonEnum::DEEP_VARIANTS,
217            &[
218                SkippedSingletonEnum::Unit,
219                SkippedSingletonEnum::Nested(UnitEnum3::A),
220                SkippedSingletonEnum::Nested(UnitEnum3::B),
221                SkippedSingletonEnum::Nested(UnitEnum3::C),
222            ]
223        );
224    }
225}
226
227#[cfg(test)]
228mod enum_from_tests
229{
230    use enum_tree_derive::EnumFrom;
231
232    #[derive(EnumFrom, PartialEq, Eq, Debug)]
233    enum TupleSingleton
234    {
235        #[expect(dead_code, reason = "exercising the EnumFrom derive on a unit variant")]
236        Unit,
237        Int(#[enum_from(from)] i32),
238        Str(#[enum_from(from)] String),
239    }
240
241    #[test]
242    fn tuple_singleton_from()
243    {
244        let a: TupleSingleton = 7_i32.into();
245        assert_eq!(a, TupleSingleton::Int(7));
246        let b: TupleSingleton = String::from("hi").into();
247        assert_eq!(b, TupleSingleton::Str("hi".to_owned()));
248    }
249
250    #[derive(PartialEq, Eq, Debug)]
251    struct Wrapper(i32);
252
253    fn wrap(n: i32) -> Wrapper
254    {
255        Wrapper(n)
256    }
257
258    #[derive(EnumFrom, PartialEq, Eq, Debug)]
259    enum ViaFunc
260    {
261        Wrapped(#[enum_from(from = i32, via = wrap)] Wrapper),
262    }
263
264    #[test]
265    fn via_func()
266    {
267        let v: ViaFunc = 5_i32.into();
268        assert_eq!(v, ViaFunc::Wrapped(Wrapper(5)));
269    }
270
271    #[derive(EnumFrom, PartialEq, Eq, Debug)]
272    enum ViaClosure
273    {
274        Doubled(#[enum_from(from = i32, via = |x| x * 2)] i32),
275    }
276
277    #[test]
278    fn via_closure()
279    {
280        let v: ViaClosure = 4_i32.into();
281        assert_eq!(v, ViaClosure::Doubled(8));
282    }
283
284    #[derive(PartialEq, Eq, Debug, Default)]
285    struct HasDefault
286    {
287        inner: i32,
288    }
289
290    fn make_has_default() -> HasDefault
291    {
292        HasDefault { inner: 42 }
293    }
294
295    #[derive(PartialEq, Eq, Debug)]
296    struct Singleton; // no default, but we should still be able to infer construction
297
298    #[derive(EnumFrom, PartialEq, Eq, Debug)]
299    enum MultiField
300    {
301        Named
302        {
303            #[enum_from(from)]
304            #[enum_from(default = || 4_i64)]
305            src: i64,
306
307            #[enum_from(default)]
308            a: HasDefault,
309
310            #[enum_from(default = make_has_default)]
311            b: HasDefault,
312
313            #[enum_from(from, default = "make_has_default")]
314            c: HasDefault,
315
316            #[enum_from(default = HasDefault { inner: 9 })]
317            d: HasDefault,
318
319            e: Singleton,
320        },
321        Unnamed(
322            #[enum_from(from)] i32,
323            #[enum_from(default)] HasDefault,
324            #[enum_from(default = HasDefault { inner: 11 })] HasDefault,
325        ),
326    }
327
328    #[test]
329    fn multi_field_named()
330    {
331        let v: MultiField = 1_i64.into();
332        assert_eq!(
333            v,
334            MultiField::Named {
335                src: 1,
336                a:   HasDefault::default(),
337                b:   HasDefault { inner: 42 },
338                c:   HasDefault { inner: 42 },
339                d:   HasDefault { inner: 9 },
340                e:   Singleton,
341            }
342        );
343        let w: MultiField = 6_i32.into();
344        assert_eq!(
345            w,
346            MultiField::Unnamed(6, HasDefault::default(), HasDefault { inner: 11 })
347        );
348
349        let x: MultiField = HasDefault { inner: 22 }.into();
350        assert_eq!(
351            x,
352            MultiField::Named {
353                src: 4,
354                a:   HasDefault::default(),
355                b:   HasDefault { inner: 42 },
356                c:   HasDefault { inner: 22 },
357                d:   HasDefault { inner: 9 },
358                e:   Singleton,
359            }
360        );
361    }
362
363    // A separate enum to test unnamed multi-field variant without conflicting
364    // From impls
365    #[derive(EnumFrom, PartialEq, Eq, Debug)]
366    enum MultiFieldUnnamed
367    {
368        Unnamed(
369            #[enum_from(from)] i64,
370            #[enum_from(default)] HasDefault,
371            #[enum_from(default = HasDefault { inner: 11 })] HasDefault,
372        ),
373    }
374
375    #[test]
376    fn multi_field_unnamed()
377    {
378        let v: MultiFieldUnnamed = 3_i64.into();
379        assert_eq!(
380            v,
381            MultiFieldUnnamed::Unnamed(3, HasDefault::default(), HasDefault { inner: 11 },)
382        );
383    }
384
385    #[derive(EnumFrom, PartialEq, Eq, Debug)]
386    enum MultipleFromSameVariant
387    {
388        Both(#[enum_from(from)] i32, #[enum_from(from)] String),
389    }
390
391    #[test]
392    fn multiple_from_same_variant()
393    {
394        let a: MultipleFromSameVariant = 5_i32.into();
395        assert_eq!(a, MultipleFromSameVariant::Both(5, String::default()));
396
397        let b: MultipleFromSameVariant = "x".to_owned().into();
398        assert_eq!(
399            b,
400            MultipleFromSameVariant::Both(i32::default(), "x".to_owned())
401        );
402    }
403}