open_enum/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#![no_std]
5#![forbid(unsafe_code)]
6
7//! Provides the [`open_enum`] macro.
8
9/// This macro creates an underlying struct that behaves like an enum, without
10/// the undefined behavior of trying to match with a value outside of the enum.
11/// The actual object created is a `#[repr(transparent)]` struct with a `pub` const
12/// value. See comment in example below for an example of the underlying
13/// structure.
14///
15/// This macro implements the following traits: `Copy`, `Clone`, `Debug`, `Eq`,
16/// `PartialEq`, `Hash`, `Ord`, `PartialOrd`.
17///
18/// An example usage case for this macro is for protocols, when you want to use
19/// an enum as a field in a struct that represents a specific type, like u16 or
20/// u32. You are also able to convert to/from bytes with this typed enum.
21///
22/// # Examples
23///
24/// ```
25/// # #[macro_use] extern crate open_enum; fn main() {
26/// use open_enum::open_enum;
27/// open_enum! {
28///     #[expect(dead_code)] // This will apply to the generated struct defn
29///     pub enum ExampleEnumName: u32 {
30///         #![expect(missing_docs)] // This will apply to all subfields of the enum
31///         THIS_IS_AN_ENUM = 32,
32///     }
33/// }
34/// // Expands to:
35/// //
36/// // #[repr(transparent)]
37//  // #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
38/// // #[expect(dead_code)]
39/// // struct ExampleEnumName(u32);
40/// //
41/// // #[expect(missing_docs)]
42/// // impl ExampleEnumName {
43/// //     pub const THIS_IS_AN_ENUM: ExampleEnumName = ExampleEnumName(0)
44/// // }
45///
46/// // To access an element in ExampleEnumName
47/// let example_enum = ExampleEnumName::THIS_IS_AN_ENUM;
48///
49/// assert_eq!(example_enum.0, 32);
50///
51/// let number: u32 = example_enum.0; // enum value is type u32
52/// # }
53/// ```
54#[macro_export]
55macro_rules! open_enum {
56    (
57        $(#[$a:meta])*
58        $v:vis enum $name:ident : $storage:ty {
59            $(#![$implattr:meta])*
60            $(
61                $(#[$vattr:meta])*
62                $variant:ident = $value:expr,
63            )*
64        }
65    ) => {
66        #[derive(Copy, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
67        #[repr(transparent)]
68        $(#[$a])*
69        $v struct $name(pub $storage);
70        $(#[$implattr])*
71        impl $name {
72            $(
73                $(#[$vattr])*
74                pub const $variant: $name = $name($value);
75            )*
76        }
77        impl ::core::fmt::Debug for $name {
78            fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
79                #![allow(unreachable_patterns)]
80                let s = match *self {
81                    $( Self::$variant => stringify!($variant), )*
82                    _ => {
83                        return ::core::fmt::Debug::fmt(&self.0, fmt);
84                    }
85                };
86                fmt.pad(s)
87            }
88        }
89    }
90}