uefi_raw/
enums.rs

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