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
// 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 can make working with enums troublesome in high performance code that can't afford premature
//! runtime checks.
//! It can also 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.
//! `open_enum` lets you have this in Rust. It 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 u8); // Automatic integer type, can be specified.
//!
//! 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 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 value 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);
//!
//! ```
//!
//! # 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
//! - 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
#![no_std]
/// 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_derive::open_enum;
/// Utility items only to be used by macros. Do not expect API stability.
#[doc(hidden)]
pub mod __private {
#[cfg(all(feature = "libc", not(feature = "std")))]
pub use libc::c_int;
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "std")]
pub use std::os::raw::c_int;
}