1use crate::{ConstDefault, ExtAny, is};
7
8impl_enum!(A:0+1, B:1+2, C:2+3, D:3+4, E:4+5, F:5+6, G:6+7, H:7+8, I:8+9, J:9+10, K:10+11, L:11+12);
10
11macro_rules! impl_enum {
16 (
17 $A:ident: $_a:literal + $a:literal,
19 $B:ident: $_b:literal + $b:literal,
20 $C:ident: $_c:literal + $c:literal,
21 $($T:ident : $idx:literal + $nth:literal),*) => {
22 impl_enum!(define_enum: $A, $B, $C $(, $T:$nth)*);
23 impl_enum!(impl_default: $A, $B, $C $(, $T)*);
24 impl_enum!(methods_general: $A:$_a+$a, $B:$_a+$b, $C:$_c+$c $(, $T:$idx+$nth)*);
25 impl_enum!(methods_individual: $A, $B, $C $(, $T)*);
26 };
27 (define_enum: $A:ident, $B:ident, $C:ident, $($rest:ident:$nth:literal),*) => { $crate::paste! {
28 #[non_exhaustive]
36 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
37 pub enum Enum<const LEN: usize, $A, $B, $C=(), $($rest = ()),*> {
38 #[doc = "The 1st variant (default)."] $A($A),
39 #[doc = "The 2nd variant."] $B($B),
40 #[doc = "The 3rd variant."] $C($C),
41 $( #[doc = "The " $nth "th variant."] $rest($rest), )*
42 }
43 }};
44 (
45 impl_default: $A:ident $(, $rest:ident)*) => {
46 impl<const LEN: usize, $A: Default, $($rest),*> Default for Enum<LEN, $A, $($rest),*> {
47 fn default() -> Self { Enum::$A($A::default()) }
48 }
49 impl<const LEN: usize, $A: ConstDefault, $($rest),*> ConstDefault
50 for Enum<LEN, $A, $($rest),*> {
51 const DEFAULT: Self = Enum::$A($A::DEFAULT);
52 }
53 };
54 (
55 methods_general: $($T:ident : $idx:literal + $nth:literal),+) => {
69 impl<const LEN:usize, $($T),+ > Enum<LEN, $($T),+> {
71 pub const LEN: usize = {
73 assert![LEN <= Self::MAX_ARITY, "LEN must be <= MAX_ARITY"];
74 LEN
75 };
76 pub const MAX_ARITY: usize = $crate::ident_total!($($T),+);
78 pub const fn variant_index(&self) -> usize {
82 match self { $( Enum::$T(_) => $idx ),+ }
83 }
84 pub const fn is_variant_index(&self, index: usize) -> bool {
86 self.variant_index() == index
87 }
88
89 pub const fn variant_name(&self) -> &'static str {
91 match self { $( Enum::$T(_) => stringify!($T) ),+ }
92 }
93 pub const fn is_variant_name(&self, name: &str) -> bool {
95 $crate::Slice::<&str>::eq(self.variant_name(), name)
96 }
97 }
98 impl<const LEN: usize, $($T: 'static),+ > Enum<LEN, $($T),+> {
99 pub fn first_non_unit() -> Option<&'static str> {
102 $( if <$T>::type_id() != <()>::type_id() { return Some(stringify!($T)); } )+
103 None
104 }
105
106 #[allow(unused_assignments, reason = "wont be read in all cases")]
109 pub fn validate() -> bool {
110 let mut non_unit_count = 0;
111 let mut unit_found = false;
112 $(
113 if <$T>::type_id() == <()>::type_id() {
114 unit_found = true;
115 } else {
116 if unit_found { return false; }
117 non_unit_count += 1;
118 }
119 )+
120 LEN == non_unit_count
121 }
122 }
123 impl<const LEN: usize, $($T: Clone),+ > Enum<LEN, $($T),+> {
125 pub fn into_tuple_options(self) -> ($(Option<$T>),+) { $crate::paste! {
127 let index = self.variant_index();
128 ( $(
129 if $idx == index {
130 self.clone().[<into_ $T:lower>]()
131 } else {
132 None::<$T>
133 }
134 ),+ )
135 }}
136
137 pub fn into_tuple_defaults(self) -> ($($T),+) where $($T: Default),+ { $crate::paste! {
140 let index = self.variant_index();
141 ( $(
142 if $idx == index {
143 self.clone().[<into_ $T:lower>]().unwrap()
144 } else {
145 Default::default()
146 }
147 ),+ )
148 }}
149 }
150 impl<const LEN: usize, $($T),+ > Enum<LEN, $($T),+> {
151 pub fn as_tuple_ref_options(&self) -> ($(Option<&$T>),+) { $crate::paste! {
155 ( $(
156 if $idx == self.variant_index() {
157 self.[<as_ref_ $T:lower>]()
158 } else {
159 None::<&$T>
160 }
161 ),+ )
162 }}
163 }
164 };
165 (
166 methods_individual: $($T:ident),+) => {
168 impl<const LEN: usize, $($T),+> Enum<LEN, $($T),+> {
170 impl_enum!(methods_field_access: $($T),+);
171 impl_enum!(methods_map: $($T),+);
172 }
173 };
174 (
175 methods_field_access: $($T:ident),+) => {
181 $( impl_enum! { @methods_field_access: $T } )+
182 };
183 (@methods_field_access: $T:ident) => { $crate::paste! {
184 #[doc = "Returns `true` if the value is of type [`" $T "`][Self::" $T "]"]
185 pub const fn [<is_ $T:lower>](&self) -> bool { matches!(self, Enum::$T(_)) }
186
187 #[doc = "Returns the inner `" $T "` value, if present."]
188 pub fn [<into_ $T:lower>](self) -> Option<$T> {
189 is![let Self::$T([<$T:lower>]) = self; Some([<$T:lower>]); None]
190 }
191 #[doc = "Returns a reference to the inner `" $T "` value, if present."]
192 pub fn [<as_ref_ $T:lower>](&self) -> Option<&$T> {
193 is![let Self::$T([<$T:lower>]) = self; Some([<$T:lower>]); None]
194 }
195 #[doc = "Returns a reference to the inner `" $T "` value, if present."]
196 pub fn [<as_mut_ $T:lower>](&mut self) -> Option<&mut $T> {
197 is![let Self::$T([<$T:lower>]) = self; Some([<$T:lower>]); None]
198 }
199 }};
200 (
201 methods_map: $first:ident $(, $rest:ident)*) => {
204 impl_enum!(@methods_map: $first, (), ($($rest),*));
206 impl_enum!(@methods_map_helper: ($first), ($($rest),*));
208
209 };
218 (
219 @methods_map: $T:ident, ( $($before:ident),* ), ( $($after:ident),* )) => { $crate::paste! {
220 #[doc = "Transforms the inner `" $T "` value using `f`, leaving other variants unchanged."]
221 pub fn [<map_ $T:lower>]<NEW>(self, f: impl FnOnce($T) -> NEW)
222 -> Enum<LEN, $($before,)* NEW, $($after,)* > {
223 match self {
224 $( Self::$before(val) => Enum::$before(val), )*
225 Self::$T(val) => Enum::$T(f(val)),
226 $( Self::$after(val) => Enum::$after(val), )*
227 }
228 }
229 }};
242 (@methods_map_helper: ($($before:ident),*), ()) => {};
244 (@methods_map_helper: ($($before:ident),*), ($first:ident $(, $rest:ident)*)) => {
246 impl_enum!(@methods_map: $first, ($($before),*), ($($rest),*));
247 impl_enum!(@methods_map_helper: ($($before,)* $first), ($($rest),*));
249 };
250}
251use impl_enum;
252
253#[cfg(test)]
254mod tests {
255 use super::Enum;
256
257 type Bytes = Enum<2, u8, i8>;
258 type Unums = Enum<4, u8, u16, u32, u64>;
259
260 #[test]
261 fn validate() {
262 assert![Bytes::validate()];
263 assert![Unums::validate()];
264 assert![Enum::<0, (), (), ()>::validate()];
265 assert![Enum::<1, i8, (), ()>::validate()];
266 assert![!Enum::<0, i8, (), ()>::validate()];
267 assert![!Enum::<2, i8, (), ()>::validate()];
268 assert![!Enum::<1, (), i8, ()>::validate()];
270 assert![!Enum::<2, i32, (), i8>::validate()];
271 assert![!Enum::<1, (), (), i8, ()>::validate()];
272 }
273 #[test]
274 fn map() {
275 let a: Enum<2, i32, f64> = Enum::A(10);
276 assert_eq![Enum::A(20), a.map_a(|v| v * 2)];
277 assert_eq![Enum::A(10), a.map_b(|v| v * 2.0)];
278 let b: Enum<2, i32, f64> = Enum::B(3.14);
279 assert_eq![Enum::B(3.14), b.map_a(|v| v * 2)];
280 assert_eq![Enum::B(6.28), b.map_b(|v| v * 2.0)];
281 }
282 #[test]
283 fn field_access() {
284 let mut u = Unums::C(32);
285 assert_eq![u.is_c(), true];
286 assert_eq![u.into_c(), Some(32)];
287 assert_eq![u.as_ref_c(), Some(&32)];
288 assert_eq![u.as_mut_c(), Some(&mut 32)];
289 assert_eq![u.is_a(), false];
291 assert_eq![u.into_a(), None];
292 assert_eq![u.as_ref_a(), None];
293 assert_eq![u.as_mut_a(), None];
294 }
295 #[test]
296 fn positioning() {
297 let u = Unums::C(32);
298 assert_eq![u.variant_index(), 2];
299 assert_eq![u.is_variant_index(2), true];
300 assert_eq![u.is_variant_index(3), false];
301 assert_eq![u.variant_name(), "C"];
302 assert_eq![u.is_variant_name("C"), true];
303 assert_eq![u.is_variant_name("B"), false];
304
305 let u = Unums::A(32);
306 assert_eq![u.variant_index(), 0];
307 assert_eq![u.is_variant_index(0), true];
308 assert_eq![u.is_variant_index(1), false];
309 assert_eq![u.variant_name(), "A"];
310 assert_eq![u.is_variant_name("A"), true];
311 assert_eq![u.is_variant_name("B"), false];
312 }
313 #[test]
314 fn tuple() {
315 let u = Unums::C(32);
316 assert_eq![
317 u.into_tuple_options(),
318 (None, None, Some(32), None, None, None, None, None, None, None, None, None)
319 ];
320 assert_eq![
321 u.as_tuple_ref_options(),
322 (None, None, Some(&32), None, None, None, None, None, None, None, None, None)
323 ];
324 assert_eq![u.into_tuple_defaults(), (0, 0, 32, 0, (), (), (), (), (), (), (), ())];
325 }
326}