fake_enum/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3//!
4//! Support for fake enum types, that act like rust enums, but all values of the underlying type
5//!  are accepted as values.
6//! See the macro [`fake_enum`] for details.
7
8///
9/// Constructs a "Fake Enum", that acts like a rust enum with unit variants,
10///  but can accept invalid (undefined) variants without undefined behaviour.
11/// The enum derives Copy, Clone, Eq, and PartialEq. Additionally, it implements Debug, where all valid variants are printed as defined,
12///  and invalid variants are formatted as name(value).
13/// Any other derives can be added following the repr.
14/// Two forms of this macro is provided. `enum name` declares an enum named "name". All the variants are declared with the same visibility as the type in the enclosing module.
15/// `enum struct name` declares an scoped enum named "name". The variants are declared `pub` within "name".
16///
17/// In Both cases, it is valid to transmute the declared type to and from the repr type (note that no from implementation is provided)
18///
19/// ```rust
20/// use fake_enum::fake_enum;
21/// fake_enum!{
22///    #[repr(u8)] pub enum Foo{
23///        Bar = 0,
24///        Baz = 1,
25///    }  
26/// };
27/// let x = Bar;
28/// assert_eq!(format!("{:?}",x),"Bar");
29/// assert_eq!(unsafe{std::mem::transmute::<_,Foo>(1u8)},Baz)
30/// ```
31///
32/// The underlying type may be given
33
34#[macro_export]
35macro_rules! fake_enum{
36    {#[repr($tvis:vis $t:ty)] $(#[$meta:meta])* $vis:vis enum $name:ident {
37        $(#![$meta1:meta])*
38        $($(#[$r:meta])* $item:ident = $expr:literal),*$(,)?
39    }} => {
40
41        #[derive(Copy,Clone,Eq,PartialEq)]
42        #[repr(transparent)]
43        $(#[$meta])*
44        $(#[$meta1])*
45        $vis struct $name($tvis $t);
46
47        $(#[allow(non_upper_case_globals)] #[allow(dead_code)] $(#[$r])* $vis const $item: $name = $name($expr as $t);)*
48
49        impl ::core::fmt::Debug for $name{
50            #[allow(unreachable_patterns)]
51            fn fmt(&self,f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result{
52                match self{
53                    $(Self($expr) => f.write_str(::core::stringify!($item)),)*
54                    e => f.write_fmt(::core::format_args!("{}({})",::core::stringify!($name),e.0))
55                }
56            }
57        }
58    };
59    {#[repr($tvis:vis $t:ty)] $(#[$meta:meta])* $vis:vis enum struct $name:ident {
60        $(#![$meta1:meta])*
61        $($(#[$r:meta])* $item:ident = $expr:literal),*$(,)?
62    }} => {
63        #[derive(Copy,Clone,Eq,PartialEq)]
64        #[repr(transparent)]
65        $(#[$meta])*
66        $(#[$meta1])*
67        $vis struct $name($tvis $t);
68        impl $name{
69            $(#[allow(non_upper_case_globals)] #[allow(dead_code)] $(#[$r])* pub const $item: $name = $name($expr as $t);)*
70        }
71        impl ::core::fmt::Debug for $name{
72            #[allow(unreachable_patterns)]
73            fn fmt(&self,f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result{
74                match self{
75                    $(Self($expr) => f.write_str(::core::stringify!($item)),)*
76                    e => f.write_fmt(::core::format_args!("{}({})",::core::stringify!($name),e.0))
77                }
78            }
79        }
80    }
81}
82
83#[cfg(test)]
84mod test {
85    fake_enum! {
86        #[repr(u16)] pub enum ElfType{
87            //! The type of an elf file
88
89            /// No Elf Type/Invalid Elf File
90            ET_NONE = 0,
91            /// Relocatable file
92            ET_REL = 1,
93            /// Executable file
94            ET_EXEC = 2,
95            /// Dynamic Library/Shared Object
96            ET_DYN = 3,
97            /// Core Dump
98            ET_CORE = 4
99        }
100    }
101
102    #[test]
103    pub fn fake_enum_elf_type_name() {
104        assert_eq!(format!("{:?}", ET_NONE), "ET_NONE");
105        assert_eq!(format!("{:?}", ET_REL), "ET_REL");
106        assert_eq!(format!("{:?}", ET_EXEC), "ET_EXEC");
107        assert_eq!(format!("{:?}", ET_DYN), "ET_DYN");
108        assert_eq!(format!("{:?}", ET_CORE), "ET_CORE");
109    }
110
111    #[test]
112    pub fn fake_enum_partial_eq_impl() {
113        assert_eq!(ET_NONE, ET_NONE);
114        assert_ne!(ET_NONE, ET_REL);
115        assert_ne!(ET_NONE, ET_EXEC);
116        assert_ne!(ET_NONE, ET_DYN);
117        assert_ne!(ET_NONE, ET_CORE);
118        assert_eq!(ET_REL, ET_REL);
119        assert_ne!(ET_REL, ET_EXEC);
120        assert_ne!(ET_REL, ET_DYN);
121        assert_ne!(ET_REL, ET_CORE);
122        assert_eq!(ET_EXEC, ET_EXEC);
123        assert_ne!(ET_EXEC, ET_DYN);
124        assert_ne!(ET_EXEC, ET_CORE);
125        assert_eq!(ET_DYN, ET_DYN);
126        assert_ne!(ET_DYN, ET_CORE);
127        assert_eq!(ET_CORE, ET_CORE);
128    }
129
130    #[test]
131    pub fn fake_enum_transmute_test() {
132        assert_eq!(unsafe { std::mem::transmute::<u16, ElfType>(0) }, ET_NONE);
133        assert_eq!(unsafe { std::mem::transmute::<u16, ElfType>(1) }, ET_REL);
134        assert_eq!(unsafe { std::mem::transmute::<u16, ElfType>(2) }, ET_EXEC);
135        assert_eq!(unsafe { std::mem::transmute::<u16, ElfType>(3) }, ET_DYN);
136        assert_eq!(unsafe { std::mem::transmute::<u16, ElfType>(4) }, ET_CORE);
137    }
138
139    fake_enum! {
140        #[repr(u8)]
141        #[derive(Hash,Default)]
142        pub enum struct NbtTagType{
143            //! The type of an Nbt Tag
144
145            /// An End Tag
146            End = 0,
147            /// A byte
148            Byte = 1,
149            /// A Short
150            Short = 2,
151            /// An Int
152            Int = 3,
153            /// A Long
154            Long = 4,
155            /// A Float
156            Float = 5,
157            Double = 6,
158            ByteArray = 7,
159            String = 8,
160            List = 9,
161            Compound = 10,
162            IntArray = 11,
163            LongArray = 12,
164            FloatArray = 13,
165            DoubleArray = 14,
166            Uuid = 15
167        }
168    }
169
170    fake_enum! {
171        #[repr(pub u8)]
172        pub enum struct Test{
173            Foo = 0
174        }
175    }
176
177    #[test]
178    fn pub_repr_test() {
179        let foo = Test(0);
180        assert_eq!(foo, Test::Foo);
181    }
182}