Skip to main content

enum_ordinalize/
lib.rs

1/*!
2# Enum Ordinalize
3
4This library enables enums to not only obtain the ordinal values of their variants but also allows for the construction of enums from an ordinal value.
5
6## Feature Flags
7
8The default features are `derive` and `traits`. The `derive` feature re-exports the `Ordinalize` derive macro, and the `traits` feature exposes the `Ordinalize` trait and enables the derive macro to implement it automatically.
9
10When only `derive` is enabled, the macro can still generate inherent constants and functions with the `#[ordinalize(...)]` attributes shown below, but it will not implement the `Ordinalize` trait.
11
12## Usage
13
14Use `#[derive(Ordinalize)]` to have an enum (which must only has unit variants) implement the `Ordinalize` trait.
15
16```rust
17# #[cfg(all(feature = "derive", feature = "traits"))]
18# {
19use enum_ordinalize::Ordinalize;
20
21#[derive(Debug, PartialEq, Eq, Ordinalize)]
22enum MyEnum {
23    Zero,
24    One,
25    Two,
26}
27
28assert_eq!(3, MyEnum::VARIANT_COUNT);
29assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two], MyEnum::VARIANTS);
30assert_eq!([0i8, 1i8, 2i8], MyEnum::VALUES);
31
32assert_eq!(0i8, MyEnum::Zero.ordinal());
33assert_eq!(1i8, MyEnum::One.ordinal());
34assert_eq!(2i8, MyEnum::Two.ordinal());
35
36assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i8));
37assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1i8));
38assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2i8));
39
40assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i8) });
41assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1i8) });
42assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2i8) });
43# }
44```
45
46#### The (Ordinal) Size of an Enum
47
48The ordinal value is an integer whose size is determined by the enum itself. The size of the enum increases with the magnitude of the variants' values, whether larger (or smaller if negative).
49
50For example,
51
52```rust
53# #[cfg(all(feature = "derive", feature = "traits"))]
54# {
55use enum_ordinalize::Ordinalize;
56
57#[derive(Debug, PartialEq, Eq, Ordinalize)]
58enum MyEnum {
59    Zero,
60    One,
61    Two,
62    Thousand = 1000,
63}
64
65assert_eq!(4, MyEnum::VARIANT_COUNT);
66assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two, MyEnum::Thousand], MyEnum::VARIANTS);
67assert_eq!([0i16, 1i16, 2i16, 1000i16], MyEnum::VALUES);
68
69assert_eq!(0i16, MyEnum::Zero.ordinal());
70assert_eq!(1i16, MyEnum::One.ordinal());
71assert_eq!(2i16, MyEnum::Two.ordinal());
72
73assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0i16));
74assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1i16));
75assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2i16));
76
77assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0i16) });
78assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1i16) });
79assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2i16) });
80# }
81```
82
83In order to accommodate the value `1000`, the size of `MyEnum` increases. Consequently, the ordinal is represented in `i16` instead of `i8`.
84
85You can utilize the `#[repr(type)]` attribute to explicitly control the size. For instance,
86
87```rust
88# #[cfg(all(feature = "derive", feature = "traits"))]
89# {
90use enum_ordinalize::Ordinalize;
91
92#[derive(Debug, PartialEq, Eq, Ordinalize)]
93#[repr(usize)]
94enum MyEnum {
95    Zero,
96    One,
97    Two,
98    Thousand = 1000,
99}
100
101assert_eq!(4, MyEnum::VARIANT_COUNT);
102assert_eq!([MyEnum::Zero, MyEnum::One, MyEnum::Two, MyEnum::Thousand], MyEnum::VARIANTS);
103assert_eq!([0usize, 1usize, 2usize, 1000usize], MyEnum::VALUES);
104
105assert_eq!(0usize, MyEnum::Zero.ordinal());
106assert_eq!(1usize, MyEnum::One.ordinal());
107assert_eq!(2usize, MyEnum::Two.ordinal());
108
109assert_eq!(Some(MyEnum::Zero), MyEnum::from_ordinal(0usize));
110assert_eq!(Some(MyEnum::One), MyEnum::from_ordinal(1usize));
111assert_eq!(Some(MyEnum::Two), MyEnum::from_ordinal(2usize));
112
113assert_eq!(MyEnum::Zero, unsafe { MyEnum::from_ordinal_unsafe(0usize) });
114assert_eq!(MyEnum::One, unsafe { MyEnum::from_ordinal_unsafe(1usize) });
115assert_eq!(MyEnum::Two, unsafe { MyEnum::from_ordinal_unsafe(2usize) });
116# }
117```
118
119Path constants, casts, binary expressions, and const fn calls used as discriminants require an explicit integer `#[repr(...)]` because the derive macro cannot evaluate them while choosing the ordinal type.
120
121#### Useful Increment
122
123The integers represented by variants can be extended in successive increments and set explicitly from any value.
124
125```rust
126# #[cfg(all(feature = "derive", feature = "traits"))]
127# {
128use enum_ordinalize::Ordinalize;
129
130#[derive(Debug, PartialEq, Eq, Ordinalize)]
131enum MyEnum {
132    Two   = 2,
133    Three,
134    Four,
135    Eight = 8,
136    Nine,
137    NegativeTen = -10,
138    NegativeNine,
139}
140
141assert_eq!(7, MyEnum::VARIANT_COUNT);
142assert_eq!([MyEnum::Two, MyEnum::Three, MyEnum::Four, MyEnum::Eight, MyEnum::Nine, MyEnum::NegativeTen, MyEnum::NegativeNine], MyEnum::VARIANTS);
143assert_eq!([2i8, 3i8, 4i8, 8i8, 9i8, -10i8, -9i8], MyEnum::VALUES);
144
145assert_eq!(4i8, MyEnum::Four.ordinal());
146assert_eq!(9i8, MyEnum::Nine.ordinal());
147assert_eq!(-9i8, MyEnum::NegativeNine.ordinal());
148
149assert_eq!(Some(MyEnum::Four), MyEnum::from_ordinal(4i8));
150assert_eq!(Some(MyEnum::Nine), MyEnum::from_ordinal(9i8));
151assert_eq!(Some(MyEnum::NegativeNine), MyEnum::from_ordinal(-9i8));
152
153assert_eq!(MyEnum::Four, unsafe { MyEnum::from_ordinal_unsafe(4i8) });
154assert_eq!(MyEnum::Nine, unsafe { MyEnum::from_ordinal_unsafe(9i8) });
155assert_eq!(MyEnum::NegativeNine, unsafe { MyEnum::from_ordinal_unsafe(-9i8) });
156# }
157```
158
159#### Implement Functionality for an enum on Itself
160
161For some reason, if you don't want to implement the `Ordinalize` trait for your enum, you can choose to disable the trait implementation and enable the constants/functions one by one. Functions are `const fn`. Names and visibility can also be defined by you.
162
163```rust
164# #[cfg(feature = "derive")]
165# {
166use enum_ordinalize::Ordinalize;
167
168#[derive(Debug, PartialEq, Eq, Ordinalize)]
169#[ordinalize(impl_trait = false)]
170#[ordinalize(variant_count(pub const VARIANT_COUNT, doc = "The count of variants."))]
171#[ordinalize(variants(pub const VARIANTS, doc = "List of this enum's variants."))]
172#[ordinalize(values(pub const VALUES, doc = "List of values for all variants of this enum."))]
173#[ordinalize(ordinal(pub const fn ordinal, doc = "Retrieve the integer number of this variant."))]
174#[ordinalize(from_ordinal(pub const fn from_ordinal, doc = "Obtain a variant based on an integer number."))]
175#[ordinalize(from_ordinal_unsafe(
176    pub const fn from_ordinal_unsafe,
177    doc = "Obtain a variant based on an integer number.",
178    doc = "# Safety",
179    doc = "You have to ensure that the input integer number can correspond to a variant on your own.",
180))]
181enum MyEnum {
182    A,
183    B,
184}
185
186assert_eq!(2, MyEnum::VARIANT_COUNT);
187assert_eq!([MyEnum::A, MyEnum::B], MyEnum::VARIANTS);
188assert_eq!([0i8, 1i8], MyEnum::VALUES);
189
190assert_eq!(0i8, MyEnum::A.ordinal());
191assert_eq!(1i8, MyEnum::B.ordinal());
192
193assert_eq!(Some(MyEnum::A), MyEnum::from_ordinal(0i8));
194assert_eq!(Some(MyEnum::B), MyEnum::from_ordinal(1i8));
195
196assert_eq!(MyEnum::A, unsafe { MyEnum::from_ordinal_unsafe(0i8) });
197assert_eq!(MyEnum::B, unsafe { MyEnum::from_ordinal_unsafe(1i8) });
198# }
199```
200*/
201
202#![no_std]
203#![cfg_attr(docsrs, feature(doc_cfg))]
204
205#[cfg(feature = "traits")]
206mod traits;
207
208#[cfg(feature = "derive")]
209pub use enum_ordinalize_derive::Ordinalize;
210#[cfg(feature = "traits")]
211pub use traits::Ordinalize;