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 and PartialEq, since that always makes sense for |
22 | /// C-style enums and is used by the implementation. 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::newtype_enum; |
34 | /// newtype_enum! { |
35 | /// #[derive(Ord, PartialOrd)] |
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)] |
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 | |