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