Skip to main content

multiboot2_common/
raw.rs

1//! Module for the `raw_type` macro.
2
3/// Defines a pair of an ABI-compatible raw newtype and a corresponding
4/// convenient, high-level open-set enum, along with all conversions between
5/// the newtype, the enum, and the underlying integer.
6///
7/// The newtype behaves like the plain integer (`Copy`, comparisons, and
8/// conversions) but carries the semantics of the enum: [`Debug`] prints the
9/// variant name together with the raw value (e.g. `Foo(0)`) and [`Display`]
10/// prints just the variant name (e.g. `Foo`); values without a specified
11/// semantic print as `Custom(x)`. It is safe to use in `#[repr(C)]`
12/// structures parsed from raw memory, as every bit pattern is valid for it. The enum assigns each specified value to a
13/// variant; all other values are mapped to the automatically added `Custom`
14/// variant, which carries the raw integer. By convention, the newtype
15/// carries the name of the enum plus a `Raw` suffix.
16///
17/// [`Debug`]: core::fmt::Debug
18/// [`Display`]: core::fmt::Display
19///
20/// # Example
21///
22/// ```
23/// multiboot2_common::raw_type! {
24///     /// ABI compatible representation of a demo type.
25///     pub struct DemoTypeRaw(u32);
26///
27///     /// The type of a demo item.
28///     ///
29///     /// This is a higher level abstraction for [`DemoTypeRaw`].
30///     pub enum DemoType {
31///         /// The first defined type.
32///         Foo = 0,
33///         /// The second defined type.
34///         Bar = 1,
35///     }
36/// }
37///
38/// let raw = DemoTypeRaw::new(1);
39/// assert_eq!(raw, DemoType::Bar);
40/// assert_eq!(DemoType::from(DemoTypeRaw::new(42)), DemoType::Custom(42));
41/// ```
42#[macro_export]
43macro_rules! raw_type {
44    (
45        $(#[$raw_attr:meta])*
46        $raw_vis:vis struct $Raw:ident($int:ty);
47
48        $(#[$enum_attr:meta])*
49        $enum_vis:vis enum $Enum:ident {
50            $(
51                $(#[$variant_attr:meta])*
52                $Variant:ident = $value:literal,
53            )+
54        }
55    ) => {
56        $(#[$raw_attr])*
57        #[repr(transparent)]
58        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
59        $raw_vis struct $Raw($int);
60
61        impl $Raw {
62            /// Constructs a new instance from the raw binary value.
63            #[must_use]
64            pub const fn new(val: $int) -> Self {
65                Self(val)
66            }
67
68            /// Returns the raw binary value.
69            #[must_use]
70            pub const fn get(self) -> $int {
71                self.0
72            }
73        }
74
75        impl ::core::fmt::Debug for $Raw {
76            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
77                ::core::fmt::Debug::fmt(&$Enum::from_val(self.0), f)
78            }
79        }
80
81        impl ::core::fmt::Display for $Raw {
82            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
83                ::core::fmt::Display::fmt(&$Enum::from_val(self.0), f)
84            }
85        }
86
87        $(#[$enum_attr])*
88        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
89        $enum_vis enum $Enum {
90            $(
91                $(#[$variant_attr])*
92                $Variant,
93            )+
94            /// Value without a specified semantic in the specification.
95            Custom($int),
96        }
97
98        impl $Enum {
99            /// Returns the raw binary value.
100            #[must_use]
101            pub const fn val(self) -> $int {
102                match self {
103                    $(Self::$Variant => $value,)+
104                    Self::Custom(val) => val,
105                }
106            }
107
108            /// Constructs the variant corresponding to the raw binary value.
109            ///
110            /// Values without a specified semantic are mapped to
111            /// [`Self::Custom`].
112            #[must_use]
113            pub const fn from_val(val: $int) -> Self {
114                match val {
115                    $($value => Self::$Variant,)+
116                    val => Self::Custom(val),
117                }
118            }
119        }
120
121        impl ::core::fmt::Debug for $Enum {
122            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
123                match self {
124                    $(Self::$Variant => f.debug_tuple(stringify!($Variant)).field(&$value).finish(),)+
125                    Self::Custom(val) => f.debug_tuple("Custom").field(val).finish(),
126                }
127            }
128        }
129
130        impl ::core::fmt::Display for $Enum {
131            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
132                match self {
133                    $(Self::$Variant => f.write_str(stringify!($Variant)),)+
134                    Self::Custom(val) => write!(f, "Custom({val})"),
135                }
136            }
137        }
138
139        impl ::core::convert::From<$int> for $Raw {
140            fn from(val: $int) -> Self {
141                Self::new(val)
142            }
143        }
144
145        impl ::core::convert::From<$Raw> for $int {
146            fn from(raw: $Raw) -> Self {
147                raw.get()
148            }
149        }
150
151        impl ::core::convert::From<$int> for $Enum {
152            fn from(val: $int) -> Self {
153                Self::from_val(val)
154            }
155        }
156
157        impl ::core::convert::From<$Enum> for $int {
158            fn from(val: $Enum) -> Self {
159                val.val()
160            }
161        }
162
163        impl ::core::convert::From<$Raw> for $Enum {
164            fn from(raw: $Raw) -> Self {
165                Self::from_val(raw.get())
166            }
167        }
168
169        impl ::core::convert::From<$Enum> for $Raw {
170            fn from(val: $Enum) -> Self {
171                Self::new(val.val())
172            }
173        }
174
175        impl ::core::cmp::PartialEq<$Enum> for $Raw {
176            fn eq(&self, other: &$Enum) -> bool {
177                self.0 == other.val()
178            }
179        }
180
181        impl ::core::cmp::PartialEq<$Raw> for $Enum {
182            fn eq(&self, other: &$Raw) -> bool {
183                self.val() == other.0
184            }
185        }
186
187        impl ::core::cmp::PartialEq<$int> for $Raw {
188            fn eq(&self, other: &$int) -> bool {
189                self.0 == *other
190            }
191        }
192
193        impl ::core::cmp::PartialEq<$Raw> for $int {
194            fn eq(&self, other: &$Raw) -> bool {
195                *self == other.0
196            }
197        }
198
199        impl ::core::cmp::PartialEq<$int> for $Enum {
200            fn eq(&self, other: &$int) -> bool {
201                self.val() == *other
202            }
203        }
204
205        impl ::core::cmp::PartialEq<$Enum> for $int {
206            fn eq(&self, other: &$Enum) -> bool {
207                *self == other.val()
208            }
209        }
210    };
211}
212
213#[cfg(test)]
214mod tests {
215    use std::collections::{BTreeSet, HashSet};
216
217    crate::raw_type! {
218        /// ABI compatible representation of a test type.
219        pub struct TestRaw(u16);
220
221        /// The type of a test item.
222        ///
223        /// This is a higher level abstraction for [`TestRaw`].
224        pub enum TestType {
225            /// The first defined value.
226            Foo = 0,
227            /// A defined value with a gap to the previous one.
228            Bar = 42,
229        }
230    }
231
232    // The newtype must be binary compatible with the underlying integer.
233    const _: () = assert!(size_of::<TestRaw>() == size_of::<u16>());
234    const _: () = assert!(align_of::<TestRaw>() == align_of::<u16>());
235
236    #[test]
237    fn test_const_constructors_and_getters() {
238        const RAW: TestRaw = TestRaw::new(42);
239        const VAL: u16 = RAW.get();
240        const TYP: TestType = TestType::from_val(VAL);
241        assert_eq!(VAL, 42);
242        assert_eq!(TYP, TestType::Bar);
243        assert_eq!(TYP.val(), 42);
244    }
245
246    /// Every raw value must be constructible and must round-trip through
247    /// the newtype and the enum, including values unknown to the
248    /// specification.
249    #[test]
250    fn test_roundtrip() {
251        for val in [0_u16, 42, 1337, u16::MAX] {
252            let raw = TestRaw::from(val);
253            let typ = TestType::from(raw);
254            assert_eq!(u16::from(raw), val);
255            assert_eq!(u16::from(typ), val);
256            assert_eq!(TestRaw::from(typ), raw);
257        }
258    }
259
260    #[test]
261    fn test_from_val() {
262        assert_eq!(TestType::from_val(0), TestType::Foo);
263        assert_eq!(TestType::from_val(42), TestType::Bar);
264        assert_eq!(TestType::from_val(7), TestType::Custom(7));
265        assert_eq!(TestType::Foo.val(), 0);
266        assert_eq!(TestType::Bar.val(), 42);
267        assert_eq!(TestType::Custom(7).val(), 7);
268    }
269
270    /// All three representations must be comparable with each other.
271    #[test]
272    fn test_partial_eq() {
273        assert_eq!(TestRaw::new(42), TestType::Bar);
274        assert_eq!(TestType::Bar, TestRaw::new(42));
275        assert_eq!(TestRaw::new(42), 42);
276        assert_eq!(42, TestRaw::new(42));
277        assert_eq!(TestType::Bar, 42);
278        assert_eq!(42, TestType::Bar);
279        assert_eq!(TestRaw::new(7), TestType::Custom(7));
280        assert_ne!(TestRaw::new(0), TestType::Bar);
281    }
282
283    /// The types must debug-print the semantic of their value together
284    /// with the raw value.
285    #[test]
286    fn test_debug() {
287        assert_eq!(format!("{:?}", TestRaw::new(0)), "Foo(0)");
288        assert_eq!(format!("{:?}", TestRaw::new(7)), "Custom(7)");
289        assert_eq!(format!("{:?}", TestType::Bar), "Bar(42)");
290    }
291
292    /// The types must display-print just the semantic of their value.
293    #[test]
294    fn test_display() {
295        assert_eq!(format!("{}", TestRaw::new(0)), "Foo");
296        assert_eq!(format!("{}", TestRaw::new(7)), "Custom(7)");
297        assert_eq!(format!("{}", TestType::Bar), "Bar");
298    }
299
300    /// Both types must be usable in ordered and hashed collections.
301    #[test]
302    fn test_ord_and_hash() {
303        let set = BTreeSet::from([TestType::Bar, TestType::Foo, TestType::Bar]);
304        assert!(set.iter().zip(set.iter().skip(1)).all(|(a, b)| a < b));
305
306        let set = HashSet::from([TestRaw::new(0), TestRaw::new(1)]);
307        assert_eq!(set.len(), 2);
308    }
309}