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 and PartialEq, since that always makes sense for
22/// C-style enums and is used by the implementation. 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::newtype_enum;
34/// newtype_enum! {
35/// #[derive(Ord, PartialOrd)]
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)]
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