1 //! This module provides tooling that facilitates dealing with C-style enums
2 //!
3 //! C-style enums and Rust-style enums are quite different. There are things
4 //! which one allows, but not the other, and vice versa. In an FFI context, two
5 //! aspects of C-style enums are particularly bothersome to us:
6 //!
7 //! - They allow a caller to send back an unknown enum variant. In Rust, the
8 //!   mere act of storing such a variant in a variable is undefined behavior.
9 //! - They have an implicit conversion to integers, which is often used as a
10 //!   more portable alternative to C bitfields or as a way to count the amount
11 //!   of variants of an enumerated type. Rust enums do not model this well.
12 //!
13 //! Therefore, in many cases, C enums are best modeled as newtypes of integers
14 //! featuring a large set of associated constants instead of as Rust enums. This
15 //! module provides facilities to simplify this kind of FFI.
16 
17 /// Interface a C-style enum as an integer newtype.
18 ///
19 /// This macro implements Debug for you, the way you would expect it to work on
20 /// Rust enums (printing the variant name instead of its integer value). It also
21 /// derives Clone, Copy, Eq, PartialEq, Ord, PartialOrd, and Hash, since that
22 /// always makes sense for C-style enums. If you want anything else
23 /// to be derived, you can ask for it by adding extra derives as shown in the
24 /// example below.
25 ///
26 /// One minor annoyance is that since variants will be translated into
27 /// associated constants in a separate impl block, you need to discriminate
28 /// which attributes should go on the type and which should go on the impl
29 /// block. The latter should go on the right-hand side of the arrow operator.
30 ///
31 /// Usage example:
32 /// ```
33 /// # use uefi_raw::newtype_enum;
34 /// newtype_enum! {
35 /// #[derive(Default)]
36 /// pub enum UnixBool: i32 => #[allow(missing_docs)] {
37 ///     FALSE          =  0,
38 ///     TRUE           =  1,
39 ///     /// Nobody expects the Unix inquisition!
40 ///     FILE_NOT_FOUND = -1,
41 /// }}
42 /// ```
43 #[macro_export]
44 macro_rules! newtype_enum {
45     (
46         $(#[$type_attrs:meta])*
47         $visibility:vis enum $type:ident : $base_integer:ty => $(#[$impl_attrs:meta])* {
48             $(
49                 $(#[$variant_attrs:meta])*
50                 $variant:ident = $value:expr,
51             )*
52         }
53     ) => {
54         $(#[$type_attrs])*
55         #[repr(transparent)]
56         #[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
57         $visibility struct $type(pub $base_integer);
58 
59         $(#[$impl_attrs])*
60         #[allow(unused)]
61         impl $type {
62             $(
63                 $(#[$variant_attrs])*
64                 pub const $variant: $type = $type($value);
65             )*
66         }
67 
68         impl core::fmt::Debug for $type {
69             fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
70                 match *self {
71                     // Display variants by their name, like Rust enums do
72                     $(
73                         $type::$variant => write!(f, stringify!($variant)),
74                     )*
75 
76                     // Display unknown variants in tuple struct format
77                     $type(unknown) => {
78                         write!(f, "{}({})", stringify!($type), unknown)
79                     }
80                 }
81             }
82         }
83     }
84 }
85