uefi_raw/
enums.rs

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]
44macro_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}