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, PartialEq, Ord, PartialOrd, and Hash, since that 22 /// always makes sense for C-style enums. 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_raw::newtype_enum; 34 /// newtype_enum! { 35 /// #[derive(Default)] 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] 44 macro_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, Ord, PartialOrd, Hash)] 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