sdmmc_core/macros/
enums.rs

1/// Convenience macro to define a library enum.
2#[macro_export]
3macro_rules! lib_enum {
4    (
5        $(#[$enum_meta:meta])+
6        $enum_ty:ident: $base_ty:ident {
7            default: $default_variant:ident,
8            error: $err_ty:ident,
9            $(
10                $(#[$var_doc:meta])*
11                $variant:ident = $value:literal$(,)?
12            )+
13        }
14    ) => {
15        paste::paste! {
16            $(#[$enum_meta])+
17            #[repr($base_ty)]
18            #[derive(Clone, Copy, Debug, Eq, PartialEq)]
19            pub enum $enum_ty {
20                $(
21                    $(#[$var_doc])*
22                    $variant = $value,
23                )+
24            }
25
26            impl $enum_ty {
27                #[doc = "Creates a new [" $enum_ty "]."]
28                pub const fn new() -> Self {
29                    Self::$default_variant
30                }
31
32                #[doc = "Converts a [`" $base_ty "`] into a [" $enum_ty "]."]
33                #[doc = ""]
34                #[doc = "# Panics"]
35                #[doc = ""]
36                #[doc = "Panics if the passed value is an invalid variant"]
37                pub const fn from_raw_unchecked(val: $base_ty) -> Self {
38                    match Self::from_raw(val) {
39                        Ok(v) => v,
40                        Err(err) => panic!("invalid variant"),
41                    }
42                }
43
44                #[doc = "Attempts to convert a [`" $base_ty "`] into a [" $enum_ty "]."]
45                pub const fn from_raw(val: $base_ty) -> ::core::result::Result<Self, $err_ty> {
46                    match val {
47                        $(
48                            $value => Ok(Self::$variant),
49                        )+
50                        _ => Err($err_ty::InvalidVariant(val as usize)),
51                    }
52                }
53
54                #[doc = "Converts a [" $enum_ty "] into a [`" $base_ty "`]."]
55                pub const fn into_raw(self) -> $base_ty {
56                    self as $base_ty
57                }
58            }
59
60            impl Default for $enum_ty {
61                fn default() -> Self {
62                    Self::new()
63                }
64            }
65
66            impl TryFrom<$base_ty> for $enum_ty {
67                type Error = $err_ty;
68
69                fn try_from(val: $base_ty) -> ::core::result::Result<Self, Self::Error> {
70                    Self::from_raw(val)
71                }
72            }
73
74            impl From<$enum_ty> for $base_ty {
75                fn from(val: $enum_ty) -> Self {
76                    val.into_raw()
77                }
78            }
79
80            #[cfg(test)]
81            mod tests {
82                use super::*;
83
84                #[test]
85                fn test_variants() {
86                    let raw = [
87                        $($value,)+
88                    ];
89
90                    let exp = [
91                        $($enum_ty::$variant,)+
92                    ];
93
94                    raw.into_iter().zip(exp).for_each(|(r, e)| {
95                        assert_eq!($enum_ty::from_raw(r), Ok(e));
96                        assert_eq!($enum_ty::try_from(r), Ok(e));
97
98                        assert_eq!(e.into_raw(), r);
99                        assert_eq!($base_ty::from(e), r);
100                    });
101                }
102
103                #[test]
104                fn test_invalid_variants() {
105                    let raw = [
106                        $($value,)+
107                    ];
108
109                    // set the upper limit to be at most 10-bits wide (1023 = 0x3ff)
110                    let upper = match $base_ty::BITS {
111                        128 | 64 | 32 | 16 => $base_ty::MAX >> ($base_ty::BITS - 10),
112                        _ => $base_ty::MAX,
113                    };
114
115                    (0..=upper).chain([$base_ty::MAX]).filter(|r| !raw.iter().any(|v| v == r)).for_each(|r| {
116                        assert_eq!($enum_ty::from_raw(r), Err($err_ty::InvalidVariant(r as usize)));
117                        assert_eq!($enum_ty::try_from(r), Err($err_ty::InvalidVariant(r as usize)));
118                    });
119                }
120            }
121        }
122    };
123}
124
125/// Convenience macro to define a library boolean enum.
126#[macro_export]
127macro_rules! lib_bool_enum {
128    (
129        $(#[$enum_meta:meta])+
130        $enum_ty:ident {
131            $(#[$false_var_doc:meta])*
132            $false_variant:ident = false,
133            $(#[$true_var_doc:meta])*
134            $true_variant:ident = true,
135        }
136    ) => {
137        paste::paste! {
138            $(#[$enum_meta])+
139            #[repr(u8)]
140            #[derive(Clone, Copy, Debug, Eq, PartialEq)]
141            pub enum $enum_ty {
142                $(#[$false_var_doc])+
143                $false_variant = 0,
144                $(#[$true_var_doc])+
145                $true_variant = 1,
146            }
147
148            impl $enum_ty {
149                #[doc = "Creates a new [" $enum_ty "]."]
150                pub const fn new() -> Self {
151                    Self::$false_variant
152                }
153
154                #[doc = "Attempts to convert a [`bool`] into a [" $enum_ty "]."]
155                pub const fn from_bool(val: bool) -> Self {
156                    match val {
157                        false => Self::$false_variant,
158                        true => Self::$true_variant,
159                    }
160                }
161
162                #[doc = "Converts a [" $enum_ty "] into a [`bool`]."]
163                pub const fn into_bool(self) -> bool {
164                    match self {
165                        Self::$false_variant => false,
166                        Self::$true_variant => true,
167                    }
168                }
169
170                #[doc = " Attempts to convert a [`u8`] into a [" $enum_ty "]."]
171                pub const fn try_from_u8(val: u8) -> $crate::result::Result<Self> {
172                    match val {
173                        0 => Ok(Self::$false_variant),
174                        1 => Ok(Self::$true_variant),
175                        _ => Err($crate::result::Error::invalid_field_variant(stringify!($enum_ty), val as usize)),
176                    }
177                }
178
179                #[doc = "Converts a [" $enum_ty "] into a [`u8`]."]
180                pub const fn into_u8(self) -> u8 {
181                    self as u8
182                }
183            }
184
185            impl Default for $enum_ty {
186                fn default() -> Self {
187                    Self::new()
188                }
189            }
190
191            impl From<bool> for $enum_ty {
192                fn from(val: bool) -> Self {
193                    Self::from_bool(val)
194                }
195            }
196
197            impl From<$enum_ty> for bool {
198                fn from(val: $enum_ty) -> Self {
199                    val.into_bool()
200                }
201            }
202
203            impl TryFrom<u8> for $enum_ty {
204                type Error = $crate::result::Error;
205
206                fn try_from(val: u8) -> ::core::result::Result<Self, Self::Error> {
207                    Self::try_from_u8(val)
208                }
209            }
210
211            impl From<$enum_ty> for u8 {
212                fn from(val: $enum_ty) -> Self {
213                    val.into_u8()
214                }
215            }
216
217            #[cfg(test)]
218            mod tests {
219                use super::*;
220
221                #[test]
222                fn test_variants() {
223                    let raw = [0, 1];
224
225                    let exp = [$enum_ty::$false_variant, $enum_ty::$true_variant];
226
227                    raw.into_iter().zip(exp).for_each(|(r, e)| {
228                        let raw_bool = r != 0;
229
230                        assert_eq!($enum_ty::from_bool(raw_bool), e);
231                        assert_eq!($enum_ty::from(raw_bool), e);
232
233                        assert_eq!($enum_ty::try_from_u8(r), Ok(e));
234                        assert_eq!($enum_ty::try_from(r), Ok(e));
235
236                        assert_eq!(e.into_bool(), raw_bool);
237                        assert_eq!(bool::from(e), raw_bool);
238
239                        assert_eq!(e.into_u8(), r);
240                        assert_eq!(u8::from(e), r);
241                    });
242                }
243
244                #[test]
245                fn test_invalid_variants() {
246                    (2..=u8::MAX).for_each(|r| {
247                        let exp_err = $crate::result::Error::invalid_field_variant(stringify!($enum_ty), r as usize);
248                        assert_eq!($enum_ty::try_from_u8(r), Err(exp_err));
249                        assert_eq!($enum_ty::try_from(r), Err(exp_err));
250                    });
251                }
252            }
253        }
254    };
255}