| //! 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) |
| } |
| } |
| } |
| } |
| } |
| } |