plain_enum/
lib.rs

1//! This crate offers some tools to deal with static enums. It offers a way to declare a simple
2//! enum, which then offers e.g. `values()` which can be used to iterate over the values of the enum.
3//! In addition, it offers a type `EnumMap` which is an array-backed map from enum values to some type.
4//!
5//! It offers a macro `plain_enum_mod` which declares an own module which contains a simple enum
6//! and the associated functionality:
7//!
8//! ```
9//! mod examples_not_to_be_used_by_clients {
10//!     #[macro_use]
11//!     use plain_enum::*;
12//!     plain_enum_mod!{example_mod_name, ExampleEnum {
13//!         V1,
14//!         V2,
15//!         SomeOtherValue,
16//!         LastValue, // note trailing comma
17//!     }}
18//!     
19//!     fn do_some_stuff() {
20//!         let map = ExampleEnum::map_from_fn(|example| // create a map from ExampleEnum to usize
21//!             example.to_usize() + 1                   // enum values convertible to usize
22//!         );
23//!         for ex in ExampleEnum::values() {            // iterating over the enum's values
24//!             assert_eq!(map[ex], ex.to_usize() + 1);
25//!         }
26//!     }
27//! }
28//! ```
29//!
30//! Internally, the macro generates a simple enum whose numeric values start counting at 0.
31
32#![recursion_limit="256"] // my tests indicate that 139 would be enough but I do not know how if that is enough in foreign code, so I chose the limit suggested by rustc
33#[macro_export]
34macro_rules! enum_seq_len {
35    () => (0);
36    ($($enumval_0: tt, $enumval_1: tt,)*) => (2*(enum_seq_len!($($enumval_0,)*)));
37    ($enumval: tt, $($enumval_0: tt, $enumval_1: tt,)*) => (1+2*(enum_seq_len!($($enumval_0,)*)));
38}
39
40#[macro_use]
41mod plain_enum {
42    macro_rules! for_each_prefix (
43        ($m:ident, [$($acc:tt,)*], []) => {
44            $m!($($acc,)*);
45        };
46        ($m:ident, [$($acc:tt,)*], [$arg0:tt, $($arg:tt,)*]) => {
47            $m!($($acc,)*);
48            for_each_prefix!($m, [$($acc,)* $arg0,], [$($arg,)*]);
49        };
50    );
51    pub trait TArrayFromFn<T> {
52        fn array_from_fn<F>(func: F) -> Self
53            where F: FnMut(usize) -> T;
54        unsafe fn index(a: &Self, e: usize) -> &T;
55        unsafe fn index_mut(a: &mut Self, e: usize) -> &mut T;
56        fn iter(a: &Self) -> slice::Iter<T>;
57        fn iter_mut(a: &mut Self) -> slice::IterMut<T>;
58        type TupleType;
59        fn from_tuple(tpl: Self::TupleType) -> Self;
60        // TODO into_tuple
61    }
62    macro_rules! ignore_first{($a0: tt, $a1: tt) => {$a1}}
63    macro_rules! impl_array_from_fn{($($i: tt,)*) => {
64        impl<T> TArrayFromFn<T> for [T; enum_seq_len!($($i,)*)] {
65            fn array_from_fn<F>(mut func: F) -> Self
66                where F: FnMut(usize) -> T
67            {
68                [$(func($i),)*]
69            }
70            #[inline(always)]
71            unsafe fn index(a: &Self, e: usize) -> &T {
72                a.get_unchecked(e)
73            }
74            #[inline(always)]
75            unsafe fn index_mut(a: &mut Self, e: usize) -> &mut T {
76                a.get_unchecked_mut(e)
77            }
78            fn iter(a: &Self) -> slice::Iter<T> {
79                a.iter()
80            }
81            fn iter_mut(a: &mut Self) -> slice::IterMut<T> {
82                a.iter_mut()
83            }
84            type TupleType = ($(ignore_first!($i, T),)*);
85            fn from_tuple(tpl: Self::TupleType) -> Self {
86                [$(tpl.$i,)*]
87            }
88        }
89    }}
90    for_each_prefix!{
91        impl_array_from_fn,
92        [0,],
93        [
94            1, 2, 3, 4, 5, 6, 7, 8, 9,
95            10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
96            20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
97            30, 31, 32, 33, 34, 35, 36, 37, 38, 39,
98            40, 41, 42, 43, 44, 45, 46, 47, 48, 49,
99            50, 51, 52, 53, 54, 55, 56, 57, 58, 59,
100            60, 61, 62, 63, 64, 65, 66, 67, 68, 69,
101            70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
102            80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
103            90, 91, 92, 93, 94, 95, 96, 97, 98, 99,
104            100, 101, 102, 103, 104, 105, 106, 107, 108, 109,
105            110, 111, 112, 113, 114, 115, 116, 117, 118, 119,
106            120, 121, 122, 123, 124, 125, 126, 127, 128,
107        ]
108    }
109    pub trait TArrayMapInto<V> {
110        type MappedType<W>;
111        fn map_into2<W>(self, f: impl FnMut(V)->W) -> Self::MappedType::<W>;
112    }
113    macro_rules! impl_array_map_into{($($val: tt,)*) => {
114        impl<V> TArrayMapInto<V> for [V; enum_seq_len!($($val,)*)] {
115            type MappedType<W> = [W; enum_seq_len!($($val,)*)];
116            fn map_into2<W>(self, mut f: impl FnMut(V)->W) -> Self::MappedType::<W> {
117                let [ $($val,)* ] = self;
118                [$(f($val),)*]
119            }
120        }
121    }}
122    for_each_prefix!{
123        impl_array_map_into,
124        [t0,],
125        [
126            t1, t2, t3, t4, t5, t6, t7, t8, t9,
127            t10, t11, t12, t13, t14, t15, t16, t17, t18, t19,
128            t20, t21, t22, t23, t24, t25, t26, t27, t28, t29,
129            t30, t31, t32, t33, t34, t35, t36, t37, t38, t39,
130            t40, t41, t42, t43, t44, t45, t46, t47, t48, t49,
131            t50, t51, t52, t53, t54, t55, t56, t57, t58, t59,
132            t60, t61, t62, t63, t64, t65, t66, t67, t68, t69,
133            t70, t71, t72, t73, t74, t75, t76, t77, t78, t79,
134            t80, t81, t82, t83, t84, t85, t86, t87, t88, t89,
135            t90, t91, t92, t93, t94, t95, t96, t97, t98, t99,
136            t100, t101, t102, t103, t104, t105, t106, t107, t108, t109,
137            t110, t111, t112, t113, t114, t115, t116, t117, t118, t119,
138            t120, t121, t122, t123, t124, t125, t126, t127, t128,
139        ]
140    }
141
142    use std;
143    use std::iter;
144    use std::ops;
145    use std::ops::{Index, IndexMut};
146    use std::slice;
147
148    pub struct SWrappedDifference<E>(pub E);
149
150    /// This trait is implemented by enums declared via the `plain_enum_mod` macro.
151    /// Do not implement it yourself, but use this macro.
152    pub unsafe trait PlainEnum : Sized {
153        /// Arity, i.e. the smallest `usize` not representable by the enum.
154        const SIZE : usize;
155        /// Internal type of enum maps.
156        type EnumMapArray<T> : TArrayFromFn<T> + TArrayMapInto<T>;
157        /// Converts `u` to the associated enum value. Assumes that `u` is a valid value for the enum, and is, thus, unsafe.
158        unsafe fn from_usize(u: usize) -> Self;
159        /// Converts the enum to its numerical representation.
160        fn to_usize(self) -> usize;
161
162        /// Checks whether `u` is the numerical representation of a valid enum value.
163        fn valid_usize(u: usize) -> bool {
164            u < Self::SIZE
165        }
166        /// Converts `u` to the associated enum value. if `u` is a valid value for the enum.
167        fn checked_from_usize(u: usize) -> Option<Self> {
168            if Self::valid_usize(u) {
169                unsafe { Some(Self::from_usize(u)) }
170            } else {
171                None
172            }
173        }
174        /// Converts `u` to the associated enum value, but wraps `u` it before conversion (i.e. it
175        /// applies the modulo operation with a modulus equal to the arity of the enum before converting).
176        fn wrapped_from_usize(u: usize) -> Self {
177            unsafe { Self::from_usize(u % Self::SIZE) }
178        }
179        /// Computes the difference between two enum values, wrapping around if necessary.
180        fn wrapped_difference_usize(self, e_other: Self) -> usize {
181            (self.to_usize() + Self::SIZE - e_other.to_usize()) % Self::SIZE
182        }
183        /// Computes the difference between two enum values, wrapping around if necessary, and converts it to an enum value.
184        fn wrapped_difference(self, e_other: Self) -> SWrappedDifference<Self> {
185            SWrappedDifference(unsafe{Self::from_usize(self.wrapped_difference_usize(e_other))})
186        }
187        /// Returns an iterator over the enum's values.
188        fn values() -> iter::Map<ops::Range<usize>, fn(usize) -> Self> {
189            (0..Self::SIZE)
190                .map(|u| unsafe { Self::from_usize(u) })
191        }
192        /// Adds a number to the enum, wrapping.
193        fn wrapping_add(self, n_offset: usize) -> Self {
194            unsafe { Self::from_usize((self.to_usize() + n_offset) % Self::SIZE) }
195        }
196        /// Creates a enum map from enum values to a type, determined by `func`.
197        /// The map will contain the results of applying `func` to each enum value.
198        fn map_from_fn<F, T>(mut func: F) -> EnumMap<Self, T>
199            where F: FnMut(Self) -> T,
200        {
201            EnumMap::from_raw(TArrayFromFn::array_from_fn(|i| func(unsafe{Self::from_usize(i)})))
202        }
203        /// Creates a enum map from a raw array.
204        fn map_from_raw<V>(a: Self::EnumMapArray::<V>) -> EnumMap<Self, V>
205            where
206        {
207            EnumMap::from_raw(a)
208        }
209        /// Creates a enum map from an appropriately sized tuple.
210        fn map_from_tuple<V>(tpl: <Self::EnumMapArray::<V> as TArrayFromFn<V>>::TupleType) -> EnumMap<Self, V>
211        {
212            EnumMap::from_tuple(tpl)
213        }
214    }
215
216    #[allow(dead_code)]
217    #[derive(Eq, PartialEq, Hash, Copy)]
218    pub struct EnumMap<E: PlainEnum, V>
219        where E: PlainEnum,
220    {
221        phantome: std::marker::PhantomData<E>,
222        a: E::EnumMapArray<V>,
223    }
224
225    impl<E, V> Clone for EnumMap<E, V>
226        where
227            E: PlainEnum,
228            V: Clone,
229    {
230        fn clone(&self) -> Self {
231            E::map_from_fn(|e| self[e].clone()) // TODO can this be improved?
232        }
233    }
234
235    impl<E, V> std::fmt::Debug for EnumMap<E, V> // TODO can this be more elegant?
236        where
237            E: PlainEnum + std::fmt::Debug,
238            V: std::fmt::Debug,
239    {
240        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
241            f
242                .debug_map()
243                .entries(E::values().map(|e| {
244                    let i = e.to_usize();
245                    let e2 = unsafe { E::from_usize(i) }; // avoids requiring Clone
246                    let e3 = unsafe { E::from_usize(i) }; // avoids requiring Clone
247                    (e2, &self[e3])
248                }))
249                .finish()
250        }
251    }
252
253    impl<V: Default, E: PlainEnum> Default for EnumMap<E, V> {
254        fn default() -> Self {
255            E::map_from_fn(|_| Default::default())
256        }
257    }
258
259    impl<E, V> EnumMap<E, V>
260        where E: PlainEnum,
261    {
262        /// Constructs an `EnumMap` from the underlying array type.
263        pub fn from_raw(a: E::EnumMapArray<V>) -> Self {
264            EnumMap{
265                phantome: std::marker::PhantomData{},
266                a,
267            }
268        }
269        pub fn from_tuple(tpl: <E::EnumMapArray<V> as TArrayFromFn<V>>::TupleType) -> Self {
270            Self::from_raw(E::EnumMapArray::<V>::from_tuple(tpl))
271        }
272        /// Returns an iterator over the values of the EnumMap. (Similar to an iterator over a slice.)
273        pub fn iter(&self) -> slice::Iter<V> {
274            TArrayFromFn::iter(&self.a)
275        }
276        /// Returns an iterator over the mutable values of the EnumMap. (Similar to an iterator over a slice.)
277        pub fn iter_mut(&mut self) -> slice::IterMut<V> {
278            TArrayFromFn::iter_mut(&mut self.a)
279        }
280        /// Maps the values in a map. (Similar to `Iterator::map`.)
281        pub fn map<FnMap, W>(&self, fn_map: FnMap) -> EnumMap<E, W>
282            where FnMap: Fn(&V) -> W,
283                  E: PlainEnum,
284        {
285            E::map_from_fn(|e|
286                fn_map(&self[e])
287            )
288        }
289        /// Moves and maps the values in a map. (Similar to `Iterator::map`.)
290        pub fn map_into<FnMap, W>(self, fn_map: FnMap) -> EnumMap<E, W>
291            where FnMap: Fn(V) -> W,
292                  E: PlainEnum,
293                  <<E as PlainEnum>::EnumMapArray<V> as TArrayMapInto<V>>::MappedType::<W>: Into<E::EnumMapArray<W>>
294        {
295            EnumMap::<E, W>::from_raw(self.a.map_into2(fn_map).into())
296        }
297        /// Consumes an `EnumMap` and returns the underlying array.
298        pub fn into_raw(self) -> E::EnumMapArray::<V> {
299            self.a
300        }
301        /// Exposes a reference to the underlying array.
302        pub fn as_raw(&self) -> &E::EnumMapArray::<V> {
303            &self.a
304        }
305        /// Exposes a mutable reference to the underlying array.
306        pub fn as_raw_mut(&mut self) -> &mut E::EnumMapArray::<V> {
307            &mut self.a
308        }
309    }
310    impl<E, V> Index<E> for EnumMap<E, V>
311        where E: PlainEnum,
312    {
313        type Output = V;
314        fn index(&self, e: E) -> &V {
315            unsafe { TArrayFromFn::index(&self.a, e.to_usize()) } // array size is E::SIZE
316        }
317    }
318    impl<E, V> IndexMut<E> for EnumMap<E, V>
319        where E: PlainEnum,
320    {
321        fn index_mut(&mut self, e: E) -> &mut Self::Output {
322            unsafe { TArrayFromFn::index_mut(&mut self.a, e.to_usize()) } // array size is E::SIZE
323        }
324    }
325
326    #[macro_export]
327    macro_rules! tt {
328        ($func: ident, [$($acc: expr,)*], []) => {
329            [$($acc,)*]
330        };
331        ($func: ident, [$($acc: expr,)*], [$enumval: ident, $($enumvals: ident,)*]) => {
332            acc_arr!($func, [$($acc,)* $func($enumval),], [$($enumvals,)*])
333        };
334    }
335
336
337    #[macro_export]
338    macro_rules! internal_impl_plainenum {($enumname: ty, $enumsize: expr, $from_usize: expr,) => {
339        unsafe impl PlainEnum for $enumname {
340            const SIZE : usize = $enumsize;
341            type EnumMapArray<T> = [T; $enumsize];
342            unsafe fn from_usize(u: usize) -> Self {
343                $from_usize(u)
344            }
345            fn to_usize(self) -> usize {
346                self as usize
347            }
348        }
349    }}
350
351    #[macro_export]
352    macro_rules! plain_enum_mod {
353        ($modname: ident, derive($($derives:ident, )*), map_derive($($mapderives:ident, )*), $enumname: ident {
354            $($enumvals: ident,)*
355        } ) => {
356            #[repr(usize)]
357            #[derive(PartialEq, Eq, Debug, Copy, Clone, PartialOrd, Ord, $($derives,)*)]
358            pub enum $enumname {
359                $(#[allow(dead_code)] $enumvals,)*
360            }
361            mod $modname {
362                use plain_enum::*;
363                use super::$enumname;
364
365                const SIZE : usize = enum_seq_len!($($enumvals,)*);
366                internal_impl_plainenum!(
367                    $enumname,
368                    SIZE,
369                    |u|{
370                        use std::mem;
371                        debug_assert!(Self::valid_usize(u));
372                        mem::transmute(u)
373                    },
374                );
375            }
376        };
377        ($modname: ident, $enumname: ident {
378            $($enumvals: ident,)*
379        } ) => {
380            plain_enum_mod!($modname, derive(), map_derive(), $enumname { $($enumvals,)* });
381        };
382    }
383}
384
385pub use plain_enum::PlainEnum;
386pub use plain_enum::EnumMap;
387pub use plain_enum::TArrayMapInto;
388
389internal_impl_plainenum!(
390    bool,
391    2,
392    |u|{
393        debug_assert!(u==0 || u==1);
394        0!=u
395    },
396);
397
398unsafe impl PlainEnum for () {
399    const SIZE : usize = 1;
400    type EnumMapArray<T> = [T; 1];
401    unsafe fn from_usize(u: usize) -> Self {
402        debug_assert_eq!(0, u);
403    }
404    fn to_usize(self) -> usize {
405        0
406    }
407}
408
409unsafe impl PlainEnum for std::cmp::Ordering {
410    const SIZE : usize = 3;
411    type EnumMapArray<T> = [T; 3];
412    // TODO: can we do better here by e.g. exploiting that Less==-1, Equal==0, Greater==1? Not sure if this is guaranteed.
413    unsafe fn from_usize(u: usize) -> Self {
414        match u {
415            0 => std::cmp::Ordering::Less,
416            1 => std::cmp::Ordering::Equal,
417            u => {
418                debug_assert_eq!(u, 2);
419                std::cmp::Ordering::Greater
420            },
421        }
422    }
423    fn to_usize(self) -> usize {
424        match self {
425            std::cmp::Ordering::Less => 0,
426            std::cmp::Ordering::Equal => 1,
427            std::cmp::Ordering::Greater => 2,
428        }
429    }
430}
431
432// TODO support Option, Result, etc.
433// TODO support nested enums
434
435#[cfg(test)]
436mod tests {
437    use plain_enum::*;
438    plain_enum_mod!{test_module, ETest {
439        E1, E2, E3,
440    }}
441    plain_enum_mod!{test_module_with_hash, derive(Hash,), map_derive(Hash,), ETestWithHash {
442        E1, E2, E3,
443    }}
444
445    #[test]
446    fn test_hash() {
447        use std::collections::HashSet;
448        let mut set = HashSet::new();
449        set.insert(ETestWithHash::E1);
450        assert!(set.contains(&ETestWithHash::E1));
451        assert!(!set.contains(&ETestWithHash::E2));
452        let enummap = ETestWithHash::map_from_fn(|e| e);
453        let mut set2 = HashSet::new();
454        set2.insert(enummap);
455    }
456
457    #[test]
458    fn test_clone() {
459        let map1 = ETest::map_from_fn(|e| e);
460        let map2 = map1.clone();
461        assert_eq!(map1, map2);
462    }
463
464    #[test]
465    fn test_enum_seq_len() {
466        assert_eq!(0, enum_seq_len!());
467        assert_eq!(1, enum_seq_len!(E1,));
468        assert_eq!(2, enum_seq_len!(E1, E3,));
469        assert_eq!(3, enum_seq_len!(E1, E2, E3,));
470        assert_eq!(14, enum_seq_len!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,));
471        assert_eq!(13, enum_seq_len!(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, ));
472    }
473
474    #[test]
475    fn test_plain_enum() {
476        assert_eq!(3, ETest::SIZE);
477    }
478
479    #[test]
480    fn test_values() {
481        assert_eq!(vec![ETest::E1, ETest::E2, ETest::E3], ETest::values().collect::<Vec<_>>());
482        assert_eq!(ETest::values().count(), 3);
483        assert_eq!((3, Some(3)), ETest::values().size_hint());
484        assert_eq!(3, ETest::values().len());
485        assert!(ETest::values().eq(ETest::values().rev().rev()));
486    }
487
488    #[test]
489    fn test_enummap() {
490        let mut map_test_to_usize = ETest::map_from_fn(|test| test.to_usize());
491        for test in ETest::values() {
492            assert_eq!(map_test_to_usize[test], test.to_usize());
493        }
494        for test in ETest::values() {
495            map_test_to_usize[test] += 1;
496        }
497        for test in ETest::values() {
498            assert_eq!(map_test_to_usize[test], test.to_usize()+1);
499        }
500        for v in map_test_to_usize.iter().zip(ETest::values()) {
501            assert_eq!(*v.0, v.1.to_usize()+1);
502        }
503        for v in map_test_to_usize.map(|n| Some(n*n)).iter() {
504            assert!(v.is_some());
505        }
506    }
507
508    #[test]
509    fn test_map_into() {
510        struct NonCopy;
511        let map_test_to_usize = ETest::map_from_fn(|_| NonCopy);
512        let _map2 : EnumMap<_, (NonCopy, usize)> =  map_test_to_usize.map_into(|noncopy| (noncopy, 0));
513    }
514
515    #[test]
516    fn test_bool() {
517        let mapbn = bool::map_from_fn(|b| b as usize);
518        assert_eq!(mapbn[false], 0);
519        assert_eq!(mapbn[true], 1);
520    }
521
522    #[test]
523    fn test_unit() {
524        let mapbn = <()>::map_from_fn(|()| 42);
525        assert_eq!(mapbn[()], 42);
526        assert_eq!(<()>::SIZE, 1);
527    }
528
529    #[test]
530    fn test_wrapped_difference() {
531        assert_eq!(ETest::E3.wrapped_difference_usize(ETest::E1), 2);
532        assert_eq!(ETest::E1.wrapped_difference_usize(ETest::E3), 1);
533        assert_eq!(ETest::E3.wrapped_difference(ETest::E1).0, ETest::E3);
534        assert_eq!(ETest::E1.wrapped_difference(ETest::E3).0, ETest::E2);
535        for e1 in ETest::values() {
536            for e2 in ETest::values() {
537                assert_eq!(e1.wrapped_difference_usize(e2), e1.wrapped_difference(e2).0.to_usize());
538            }
539        }
540    }
541
542    #[test]
543    fn test_default() {
544        let _enummap : EnumMap<ETest, usize> = Default::default();
545    }
546
547    #[test]
548    fn test_from_tuple() {
549        let enummap = ETest::map_from_tuple((1,2,3));
550        assert_eq!(enummap[ETest::E1], 1);
551        assert_eq!(enummap[ETest::E2], 2);
552        assert_eq!(enummap[ETest::E3], 3);
553    }
554}
555