numeric_enum_macro/lib.rs
1//! A declarative macro for type-safe enum-to-numbers conversion. `no-std` supported!
2//!
3//! ```
4//! use numeric_enum_macro::numeric_enum;
5//!
6//! numeric_enum! {
7//! #[repr(i64)] // repr must go first.
8//! /// Some docs.
9//! ///
10//! /// Multiline docs works too.
11//! #[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] // all the attributes are forwarded!
12//! pub enum Lol {
13//! // All the constants must have explicit values assigned!
14//! Kek = 14,
15//! Wow = 87,
16//! }
17//! }
18//!
19//! const KEK: u32 = 0;
20//! const WOW: u32 = 1;
21//!
22//! numeric_enum! {
23//! #[repr(u32)] // repr must go first.
24//! /// Some docs.
25//! ///
26//! /// Multiline docs works too.
27//! #[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Hash)] // all the attributes are forwarded!
28//! pub enum Lol2 {
29//! /// This is KEK
30//! Kek = KEK,
31//! /// And this is WOW
32//! Wow = WOW,
33//! }
34//! }
35//!
36//! # use ::core::convert::TryFrom;
37//! // Conversion to raw number:
38//! assert_eq!(14i64, Lol::Kek.into());
39//! // Conversion from raw number:
40//! assert_eq!(Ok(Lol::Wow), Lol::try_from(87));
41//! // Unknown number:
42//! assert_eq!(Err(88), Lol::try_from(88));
43//!
44//! assert_eq!(Ok(Lol2::Wow), Lol2::try_from(WOW));
45//! ```
46
47#![no_std]
48
49/// Declares an enum with a given numeric representation defined by literals.
50///
51/// Only explicitly enumerated enum constants are supported.
52///
53/// Automatically derives `TryFrom<$repr>` and `From<$name>`.
54///
55/// For examples look at the crate root documentation.
56#[macro_export]
57macro_rules! numeric_enum {
58 (#[repr($repr:ident)]
59 $(#$attrs:tt)* $vis:vis enum $name:ident {
60 $($(#$enum_attrs:tt)* $enum:ident = $constant:expr),* $(,)?
61 } ) => {
62 #[repr($repr)]
63 $(#$attrs)*
64 $vis enum $name {
65 $($(#$enum_attrs)* $enum = $constant),*
66 }
67
68 impl ::core::convert::TryFrom<$repr> for $name {
69 type Error = $repr;
70
71 fn try_from(value: $repr) -> ::core::result::Result<Self, $repr> {
72 $(if $constant == value { return Ok($name :: $enum); } )*
73 Err(value)
74 }
75 }
76
77 impl ::core::convert::From<$name> for $repr {
78 fn from(value: $name) -> $repr {
79 match value {
80 $($name :: $enum => $constant,)*
81 }
82 }
83 }
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 numeric_enum! {
92 #[repr(i16)]
93 /// Documentation.
94 ///
95 /// Multiline.
96 #[derive(Debug, PartialEq, Eq)]
97 pub enum PublicEnum { Zero = 0, Lol = -1 }
98 }
99
100 numeric_enum! {
101 #[repr(u8)]
102 enum TrailingComa { A = 0, B = 1, }
103 }
104
105 numeric_enum! {
106 #[repr(u8)]
107 enum NoTrailingComa { A = 0, B = 1 }
108 }
109
110 const ZERO: u8 = 0;
111 const LOL: u8 = 1;
112
113 numeric_enum! {
114 #[repr(u8)]
115 enum PrivateEnum {
116 Zero = ZERO,
117 Lol = LOL,
118 }
119 }
120
121 #[test]
122 fn it_works() {
123 use core::convert::TryFrom;
124
125 assert_eq!(-1i16, PublicEnum::Lol.into());
126 assert_eq!(PublicEnum::try_from(0), Ok(PublicEnum::Zero));
127 assert_eq!(PublicEnum::try_from(-1), Ok(PublicEnum::Lol));
128 assert_eq!(PublicEnum::try_from(2), Err(2));
129 }
130}