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}