devela/code/util/
enumset.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
// devela::code::util::enumset
//
//! An enum with an associated bitfield.
//

/// Defines an enum and an associated type set of its variants.
///
/// It uses the [`bitfield!`][crate::data::bitfield] macro to create the associated set.
///
/// You have to give unique names both to the enum and to the associated set.
///
/// # Features
/// This macro depends on enabling any of the `_bit` features. E.g. `_bit_u8`.
///
/// # Examples
/// See also the [enumset][crate::_info::examples::enumset] example.
///
/// ```
/// # use devela::enumset;
/// enumset! {
///     pub enum MyEnum(pub MyEnumSet: u8) {
///         Variant1,
///         Variant2(bool),
///         Variant3{a: u8, b: u16},
///     }
/// }
/// assert_eq![3, MyEnum::ENUM_VARIANTS];
/// let mut eset = MyEnumSet::default();
/// assert![eset.is_empty()];
/// eset.mut_set_field_variant1();
/// assert![eset.is_field_variant1()];
/// ```
#[doc(hidden)]
#[macro_export]
macro_rules! _enumset {
    (
        // $enum_attr: the attributes of the enum.
        // $enum_vis:  the visibility of the enum.
        // $enum_name: the name of the new enum.
        // $set_attr:  the attributes of the set.
        // $set_vis:   the visibility of the set.
        // $set_name:  the name of the associated set.
        // $set_ty:    the inner integer primitive type for the bitfield (u8, i32, …).
        $( #[$enum_attr:meta] )*
        $enum_vis:vis enum $enum_name:ident
            $( < $($gen:tt),* $(,)? > )? // optional generics and lifetimes
            // attributes, visibility, name and inner type of the set, between ():
            ( $( #[$set_attr:meta] )* $set_vis:vis $set_name:ident: $set_ty:ty )
            $([where $($where:tt)+ $(,)? ] $(,)? )? // optional where clauses, between []
        {
            $(
                $( #[$variant_attr:meta] )*
                $variant_name:ident
                $(( $($tuple_type:ty),* $(,)? ))?
                $({ $( $( #[$field_attr:meta] )* $field_name:ident : $field_type:ty),* $(,)? })?
                $(= $discriminant:expr)?
                $(,)?
            )*
        }
    ) => { $crate::paste! {
        /* define enum */

        $( #[$enum_attr] )*
        // #[doc = "\n\nSee also the associated type set of variants [`" $set_name "`]."]
        $enum_vis enum $enum_name $( < $($gen),* > )? $(where $($where)+)? {
            $(
                $( #[$variant_attr] )*
                $variant_name
                $(( $($tuple_type),* ))?
                $({ $( $( #[$field_attr] )* $field_name : $field_type),* })?
                $(= $discriminant)?
            ),*
        }

        /* define the associated bitfield */

        #[allow(non_snake_case)]
        mod [<_$enum_name _private>] {
            pub(super) const ENUM_VARIANTS: usize = $crate::ident_total!($($variant_name)*);
            $crate::ident_const_index!(pub(super), ENUM_VARIANTS; $($variant_name)*);
        }

        /// # `enumset` methods
        #[allow(dead_code)]
        impl $( < $($gen),* > )? $enum_name $( < $($gen),* > )? $( where $($where)* )? {
            /// Returns the total number of variants.
            $set_vis const ENUM_VARIANTS: usize = [<_$enum_name _private>]::ENUM_VARIANTS;

            /// Returns the total number of variants.
            $set_vis const fn enum_variants(&self) -> usize { Self::ENUM_VARIANTS }

            /// Returns the associated empty set.
            $set_vis const fn new_empty_set() -> $set_name {
                $set_name::without_fields()
            }
            /// Returns the associated full set.
            $set_vis const fn new_full_set() -> $set_name {
                $set_name::with_all_fields()
            }
        }

        $crate::data::bitfield! { // NOTE: need the long path
            $( #[$set_attr] )*
            // #[doc = "Represents a set of [`" $enum_name "`] variants."]
            $set_vis struct $set_name($set_ty) {
                $(
                    #[doc = "The bit index that corresponds to `" $enum_name "::`[`"
                        $variant_name "`][" $enum_name "::" $variant_name "]."]

                    #[allow(non_upper_case_globals)]
                    $variant_name: [<_$enum_name _private>]::$variant_name as u32;
                )*
            }
        }
    }};
}
#[doc(inline)]
#[cfg_attr(feature = "nightly_doc", doc(cfg(_bit_·)))]
pub use _enumset as enumset;