1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Rust enums are _closed_, meaning that the integer value distinguishing an enum, its _discriminant_,
//! must be one of the variants listed. If the integer value isn't one of those discriminants, it
//! is considered immediate [undefined behavior][ub]. This is true for enums with and without fields.
//!
//! This has some disadvantages:
//! - in constrained environments, closed enums can require premature runtime checks when using
//! `TryFrom` to convert from an integer. This is doubly true if the value will be checked again
//! at a later point, such as with a C library.
//! - an outdated binary using an enum won't preserve the value of an unknown field when reserializing
//! data without an extra `Unrecognized` value making the type more expensive than an integer.
//! - it can introduce Undefined Behavior at unexpected times if the author is unfamiliar with
//! the [rules of writing `unsafe` Rust][nomicon].
//!
//! In constrast, C++ [scoped enumerations][cpp-scoped-enum] are _open_, meaning that the enum is a
//! strongly-typed integer that could hold any value, though with a scoped set of well-known values.
//!
//! The _open enum_ pattern lets you have this in Rust. With a [newtype][newtype] and associated constants,
//! the [open_enum][open_enum] macro turns this enum declaration:
//!
//! ```
//! # use open_enum::open_enum;
//! #[open_enum]
//! enum Color {
//! Red,
//! Green,
//! Blue,
//! Orange,
//! Black,
//! }
//! ```
//!
//! into a tuple struct with associated constants:
//!
//! ```
//! #[derive(PartialEq, Eq)] // In order to work in `match`.
//! struct Color(pub i8); // Automatic integer type, can be specified with #[repr]
//!
//! impl Color {
//! pub const Red: Self = Color(0);
//! pub const Green: Self = Color(1);
//! pub const Blue: Self = Color(2);
//! pub const Orange: Self = Color(3);
//! pub const Black: Self = Color(4);
//! }
//! ```
//!
//! There are clear readability benefits to using field-less `enum`s to represent enumerated integer data.
//! It provides more type safety than a raw integer, the `enum` syntax is consise, and it provides a
//! set of constants grouped under a type that can have methods.
//!
//! # Usage
//! Usage is similar to regular `enum`s, but with some key differences.
//!
//! ```
//! # use open_enum::open_enum;
//! # #[open_enum]
//! # #[derive(Debug)]
//! # enum Color {
//! # Red,
//! # Green,
//! # Blue,
//! # Orange,
//! # Black,
//! # }
//! // Construct an open enum with the same `EnumName::VariantName` syntax.
//! let mut blood_of_angry_men = Color::Red;
//!
//! // Access the integer value with `.0`.
//! // This does not work: `Color::Red as u8`.
//! assert_eq!(blood_of_angry_men.0, 0);
//!
//! // Construct an open enum with an arbitrary integer value like any tuple struct.
//! let dark_of_ages_past = Color(4);
//!
//! // open enums always implement `PartialEq` and `Eq`, unlike regular enums.
//! assert_eq!(dark_of_ages_past, Color::Black);
//!
//! // This is outside of the known colors - but that's OK!
//! let this_is_fine = Color(10);
//!
//! // A match is always non-exhaustive - requiring a wildcard branch.
//! match this_is_fine {
//! Color::Red => panic!("a world about to dawn"),
//! Color::Green => panic!("grass"),
//! Color::Blue => panic!("蒼: not to be confused with 緑"),
//! Color::Orange => panic!("fun fact: the fruit name came first"),
//! Color::Black => panic!("the night that ends at last"),
//! // Wildcard branch, if we don't recognize the value. `x =>` also works.
//! Color(value) => assert_eq!(value, 10),
//! }
//!
//! // Unlike a regular enum, you can pass the discriminant as a reference.
//! fn increment(x: &mut i8) {
//! *x += 1;
//! }
//!
//! increment(&mut blood_of_angry_men.0);
//! // These aren't men, they're skinks!
//! assert_eq!(blood_of_angry_men, Color::Green);
//!
//! ```
//!
//! ## Integer type
//! `open_enum` will automatically determine an appropriately sized integer[^its-all-isize] to
//! represent the enum, if possible[^nonliterals-are-hard]. To choose a specific representation, it's the same
//! as a regular `enum`: add `#[repr(type)]`.
//! You can also specify `#[repr(C)]` to choose a C `int`.[^repr-c-feature][^repr-c-weird]
//!
//! If you specify an explicit `repr`, the output struct will be `#[repr(transparent)]`.
//!
//! ```
//! # use open_enum::open_enum;
//! #[open_enum]
//! #[repr(i16)]
//! #[derive(Debug)]
//! enum Fruit {
//! Apple,
//! Banana,
//! Kumquat,
//! Orange,
//! }
//!
//! assert_eq!(Fruit::Banana.0, 1i16);
//! assert_eq!(Fruit::Kumquat, Fruit(2));
//!
//! ```
//! <div class="example-wrap" style="display:inline-block"><pre class="compile_fail" style="white-space:normal;font:inherit;">
//!
//! **Warning**: `open_enum` may change the automatic integer representation for a given enum
//! in a future version with a minor version bump - it is not considered a breaking change.
//! Do not depend on this type remaining stable - use an explicit `#[repr]` for stability.
//!
//! </pre></div>
//!
//! [^its-all-isize]: Like regular `enum`s, the declared discriminant for enums without an explicit `repr`
//! is interpreted as an `isize` regardless of the automatic storage type chosen.
//!
//! [^nonliterals-are-hard]: This optimization fails if the `enum` declares a non-literal constant expression
//! as one of its discriminant values, and falls back to `isize`. To avoid this, specify an explicit `repr`.
//!
//! [^repr-c-weird]: Note that this might not actually be the correct default `enum` size for C on all platforms,
//! since the [compiler could choose something smaller than `int`](https://stackoverflow.com/a/366026).
//!
//! [^repr-c-feature]: This requires either the `std` or `libc_` feature (note the underscore)
//!
//! ## Aliasing variants
//! Regular `enum`s cannot have multiple variants with the same discriminant.
//! However, since `open_enum` produces associated constants, multiple
//! names can represent the same integer value. By default, `open_enum`
//! rejects aliasing variants, but it can be allowed with the `allow_alias` option:
//!
//! ```
//! # use open_enum::open_enum;
//! #[open_enum(allow_alias)]
//! #[derive(Debug)]
//! enum Character {
//! Viola = 0,
//! Cesario = 0,
//! Sebastian,
//! Orsino,
//! Olivia,
//! Malvolio,
//! }
//!
//! assert_eq!(Character::Viola, Character::Cesario);
//!
//! ```
//!
//!
//!
//! # Custom debug implementation
//! `open_enum` will generate a debug implementation that mirrors the standard `#[derive(Debug)]` for normal Rust enums
//! by printing the name of the variant rather than the value contained, if the value is a named variant.
//!
//! However, if an enum has `#[open_enum(allow_alias)]` specified, the debug representation will be the numeric value only.
//!
//! For example, this given enum,
//! ```
//! # use open_enum::open_enum;
//! #[open_enum]
//! #[derive(Debug)]
//! enum Fruit {
//! Apple,
//! Pear,
//! Banana,
//! Blueberry = 5,
//! Raspberry,
//! }
//! ```
//!
//! will have the following debug implementation emitted:
//! ```
//! # use open_enum::open_enum;
//! # #[open_enum]
//! # enum Fruit {
//! # Apple,
//! # Pear,
//! # Banana,
//! # Blueberry = 5,
//! # Raspberry,
//! # }
//! # impl ::core::fmt::Debug for Fruit {
//! fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
//! #![allow(unreachable_patterns)]
//! let s = match *self {
//! Self::Apple => stringify!(Apple),
//! Self::Pear => stringify!(Pear),
//! Self::Banana => stringify!(Banana),
//! Self::Blueberry => stringify!(Blueberry),
//! Self::Raspberry => stringify!(Raspberry),
//! _ => {
//! return fmt.debug_tuple(stringify!(Fruit)).field(&self.0).finish();
//! }
//! };
//! fmt.pad(s)
//! }
//! # }
//! ```
//!
//! # Compared with `#[non_exhuastive]`
//! The [`non_exhaustive`][non-exhaustive] attribute indicates that a type or variant
//! may have more fields or variants added in the future. When applied to an `enum` (not its variants),
//! it requires that foreign crates provide a wildcard arm when `match`ing.
//! Since open enums are inherently non-exhaustive[^mostly-non-exhaustive], this attribute is incompatible
//! with `open_enum`. Unlike `non_exhaustive`, open enums also require a wildcard branch on `match`es in
//! the defining crate.
//!
//! [^mostly-non-exhaustive]: Unless the enum defines a variant for every value of its underlying integer.
//!
//! # Disadvantages of open enums
//! - The kind listed in the source code, an `enum`, is not the same as the actual output, a `struct`,
//! which could be confusing or hard to debug, since its usage is similar, but not exactly the same.
//! - No niche optimization: `Option<Color>` is 1 byte as a regular enum,
//! but 2 bytes as an open enum.
//! - No pattern-matching assistance in rust-analyzer.
//! - You must have a wildcard case when pattern matching.
//! - `match`es that exist elsewhere won't break when you add a new variant,
//! similar to `#[non_exhaustive]`. However, it also means you may accidentally
//! forget to fill out a branch arm.
//!
//!
//! [cpp-scoped-enum]: https://en.cppreference.com/w/cpp/language/enum#Scoped_enumerations
//! [nomicon]: https://doc.rust-lang.org/nomicon/
//! [non-exhaustive]: https://doc.rust-lang.org/reference/attributes/type_system.html#the-non_exhaustive-attribute
//! [ub]: https://doc.rust-lang.org/reference/behavior-considered-undefined.html
/// Constructs an *open* enum from a Rust enum definition,
/// allowing it to represent more than just its listed variants.
///
/// See the [crate documentation](crate) for more details.
///
/// # Example
/// ```
/// # use open_enum::open_enum;
/// #[open_enum]
/// #[derive(Debug)]
/// enum Color {
/// Red,
/// Green,
/// Blue,
/// Orange,
/// Black,
/// }
///
/// assert_eq!(Color::Red, Color(0));
/// assert_eq!(Color(10).0, 10);
/// ```
///
/// # Options
/// - `allow_alias[ = $bool]`: default `false`. Allows duplicate discriminant values for variants.
/// - `inner_vis = $vis`: default `pub`. Specifies the visibility of the inner integer.
///
/// # Integer type
/// `open_enum` configures the discriminant type by intercepting a `repr` attribute on the enum.
/// If done, the open enum is `#[repr(transparent)]` over the provided integer type.
/// Otherwise, variant discriminants are interpreted as `isize` and an automatic integer type chosen.
///
/// # `PartialEq`/`Eq`
/// Open enums implement `PartialEq` and `Eq` in order to work in a `match` statement.
pub use open_enum;
/// Utility items only to be used by macros. Do not expect API stability.