assert_enum_variants/lib.rs
1#![no_std]
2#![doc = include_str!("../README.md")]
3
4/// This macro performs a compile-time check to validate that all variants of an enum
5/// are as provided in the macro invocation.
6///
7/// # Example
8///
9/// ```rust
10/// use assert_enum_variants::assert_enum_variants;
11///
12/// #[allow(dead_code)]
13/// pub enum MyEnum {
14/// A,
15/// B(u32),
16/// C {
17/// a: String,
18/// b: u32,
19/// },
20/// }
21///
22/// // This will compile successfully
23/// // because all variants of `MyEnum` are accounted for.
24/// assert_enum_variants!(MyEnum, { A, B, C });
25/// ```
26///
27/// It will fail to compile if any of the variants are missing or if there are any
28/// extra variants.
29///
30/// # Example of faliure due to missing variants
31///
32/// ```rust,compile_fail
33/// use assert_enum_variants::assert_enum_variants;
34///
35/// #[allow(dead_code)]
36/// pub enum MyEnum {
37/// A,
38/// B(u32),
39/// C {
40/// a: String,
41/// b: u32,
42/// },
43/// }
44///
45/// // This will fail to compile
46/// // because the `C` variant is missing.
47/// assert_enum_variants!(MyEnum, { A, B });
48///```
49///
50/// # Example of failure due to extra variants
51///
52/// ```rust,compile_fail
53/// use assert_enum_variants::assert_enum_variants;
54///
55/// #[allow(dead_code)]
56/// pub enum MyEnum {
57/// A,
58/// B(u32),
59/// C {
60/// a: String,
61/// b: u32,
62/// },
63/// }
64///
65/// // This will fail to compile
66/// // because the `D` variant is not present on `MyEnum`.
67/// assert_enum_variants!(MyEnum, { A, B, C, D });
68/// ```
69///
70/// # Reasons for using this macro
71///
72/// Let's say you're writing some code that needs to handle all variants of an enum
73/// but there could be a situation that none of the variants fits.
74///
75/// ```rust
76/// enum ResumeFileFormat {
77/// Pdf,
78/// Docx,
79/// Doc,
80/// }
81///
82/// // ...
83///
84/// impl ResumeFileFormat {
85/// fn from_extension(ext: &str) -> Option<Self> {
86/// use ResumeFileFormat::{Pdf, Docx, Doc};
87///
88/// let file_format: ResumeFileFormat = match ext {
89/// "pdf" => Pdf,
90/// "docx" => Docx,
91/// "doc" => Doc,
92/// _ => return None,
93/// };
94///
95/// Some(file_format)
96/// }
97/// }
98/// ```
99///
100/// Notice that due to a wildcard pattern, the compiler will not warn you if you
101/// add a new variant to the enum and forget to modify the `from_extension`.
102///
103/// That is, unless you use the [`assert_enum_variants!`] macro.
104///
105/// The following code will fail to compile if you add a new variant to the enum
106/// and forget to modify the `from_extension` function.
107///
108/// ```rust,compile_fail
109/// use assert_enum_variants::assert_enum_variants;
110///
111/// enum ResumeFileFormat {
112/// Pdf,
113/// Docx,
114/// Doc,
115/// Json,
116/// }
117///
118/// // ...
119///
120/// impl ResumeFileFormat {
121/// fn from_extension(ext: &str) -> Option<Self> {
122/// use ResumeFileFormat::{Pdf, Docx, Doc};
123///
124/// // This will fail to compile because the `Json` variant is missing.
125/// assert_enum_variants!(ResumeFileFormat, { Pdf, Docx, Doc });
126///
127/// let file_format: ResumeFileFormat = match ext {
128/// "pdf" => Pdf,
129/// "docx" => Docx,
130/// "doc" => Doc,
131/// _ => return None,
132/// };
133///
134/// Some(file_format)
135/// }
136/// }
137/// ```
138#[macro_export]
139macro_rules! assert_enum_variants {
140 ($enum:path, { $($variant:ident),* $(,)? }) => {
141 const _: () = {
142 #[allow(unreachable_code)]
143 if false {
144 let _unreachable_obj: $enum = core::unreachable!();
145
146 #[allow(unused_imports)]
147 use $enum::{ $($variant),* };
148
149 match _unreachable_obj {
150 $(
151 $variant { .. } => (),
152 )*
153 };
154 }
155 };
156 }
157}
158
159#[cfg(test)]
160mod tests {
161 mod my_mod {
162 #[allow(dead_code)]
163 pub enum MyEnum {
164 A,
165 B(u32),
166 C { a: String, b: u32 },
167 }
168 }
169
170 #[allow(dead_code)]
171 enum Never {}
172
173 #[test]
174 fn test_enum_variants() {
175 assert_enum_variants!(my_mod::MyEnum, { A, B, C });
176 assert_enum_variants!(Never, {});
177 }
178}