1#[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 #[must_use]
64 pub const fn new(val: $int) -> Self {
65 Self(val)
66 }
67
68 #[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 Custom($int),
96 }
97
98 impl $Enum {
99 #[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 #[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 pub struct TestRaw(u16);
220
221 pub enum TestType {
225 Foo = 0,
227 Bar = 42,
229 }
230 }
231
232 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 #[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 #[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 #[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 #[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 #[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}