enum_tools/lib.rs
1#![forbid(unsafe_code)]
2#![warn(missing_docs)]
3#![allow(rustdoc::redundant_explicit_links)]
4
5//! Automatically derive functions and trait implementations for enums.
6//!
7//! The enum must be field-less, has a primitive representation and be [`Copy`](::std::marker::Copy).
8//!
9//! Many helpful function can be derived:
10//!
11//! - Into/TryFrom primitive
12//! - iterator over all items and/or names
13//! - next/previous item
14//! - from/to string
15//! - and more
16//!
17//! For the full documentation see [`EnumTools`](crate::EnumTools).
18//!
19//! # Example
20//!
21//! ```
22//! # use enum_tools::EnumTools;
23//! #[derive(Clone, Copy, EnumTools)]
24//! // features which create function/constant
25//! #[enum_tools(as_str, from_str, into, MAX, MIN, next, next_back, try_from)]
26//! // features which implement a trait
27//! #[enum_tools(Debug, Display, FromStr, Into, IntoStr, TryFrom)]
28//! // features which create a iterator (function and struct with impl)
29//! #[enum_tools(iter, names, range)]
30//! #[repr(i8)]
31//! pub enum MyEnum { A=0, B=5, C=1 }
32//! ```
33//!
34//! Derives something similar as below
35//!
36//! ```
37//! # pub enum MyEnum { A=0, B=5, C=1 }
38//! // functions on the enum
39//! impl MyEnum {
40//! pub const MIN : Self = MyEnum::A;
41//! pub const MAX : Self = MyEnum::B;
42//! pub fn as_str(self) -> &'static str
43//! # {todo!()}
44//! pub fn from_str(s: &str) -> Option<Self>
45//! # {todo!()}
46//! pub const fn into(self) -> i8 { self as i8 }
47//! pub fn next(self) -> Option<Self> // the next element by value (or None if last)
48//! # {todo!()}
49//! pub fn next_back(self) -> Option<Self> // the previous element by value (or None if first)
50//! # {todo!()}
51//! pub fn try_from(value: i8) -> Option<Self>
52//! # {todo!()}
53//!
54//! pub fn iter() -> MyEnumIter // a iterator over all elements by value
55//! # {todo!()}
56//! pub fn names() -> MyEnumNames // a iterator over all names by value
57//! # {todo!()}
58//! pub fn range(start: Self, end: Self) -> MyEnumIter // similar to `..=`
59//! # {todo!()}
60//! }
61//!
62//! # use core::fmt::{Debug, Display, Formatter};
63//! # use core::str::FromStr;
64//! # use core::iter::FusedIterator;
65//! #
66//! // implementations on the enum
67//! impl Debug for MyEnum // calls `as_str`
68//! # {fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { todo!() }}
69//! impl Display for MyEnum // calls `as_str`
70//! # {fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { todo!() }}
71//! impl From<MyEnum> for i8 // feature "Into"
72//! # {fn from(_: MyEnum) -> Self { todo!() }}
73//! impl From<MyEnum> for &'static str // feature "IntoStr", calls "as_str"
74//! # {fn from(_: MyEnum) -> Self { todo!() }}
75//! impl FromStr for MyEnum
76//! # {type Err = (); fn from_str(s: &str) -> Result<Self, Self::Err> { todo!() }}
77//! impl TryFrom<i8> for MyEnum
78//! # {type Error = (); fn try_from(value: i8) -> Result<Self, Self::Error> { todo!() }}
79//!
80//! // structs and impls for the iterators
81//! pub struct MyEnumIter
82//! # (());
83//! impl Iterator for MyEnumIter
84//! # {type Item = (); fn next(&mut self) -> Option<Self::Item> { todo!() }}
85//! impl DoubleEndedIterator for MyEnumIter
86//! # {fn next_back(&mut self) -> Option<Self::Item> { todo!() }}
87//! impl ExactSizeIterator for MyEnumIter
88//! # {}
89//! impl FusedIterator for MyEnumIter
90//! # {}
91//! pub struct MyEnumNames
92//! # (());
93//! impl Iterator for MyEnumNames
94//! # {type Item = (); fn next(&mut self) -> Option<Self::Item> { todo!() }}
95//! impl DoubleEndedIterator for MyEnumNames
96//! # {fn next_back(&mut self) -> Option<Self::Item> { todo!() }}
97//! impl ExactSizeIterator for MyEnumNames
98//! # {}
99//! impl FusedIterator for MyEnumNames
100//! # {}
101//! ```
102use proc_macro_error::proc_macro_error;
103use syn::{parse_macro_input, DeriveInput};
104
105mod feature;
106mod generator;
107mod parser;
108
109/// Derive Macro for enums
110///
111/// # Requirements
112///
113/// This derive macro only works on enum
114/// - must be [`Copy`](::std::marker::Copy)
115/// - must be [field-less](https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations)
116/// - must have a [primitive representation](https://doc.rust-lang.org/reference/type-layout.html#primitive-representations)
117/// - all values must be within `[i64::MIN, i64::MAX]` (independent of the internal representation)
118/// - at most `u16::MAX-1` items
119///
120/// # Example
121///
122/// ```
123/// # use enum_tools::EnumTools;
124/// #[derive(Clone, Copy, EnumTools, PartialEq)]
125/// #[enum_tools(as_str, Debug, next, next_back(name = "pred"))]
126/// #[repr(usize)]
127/// pub enum MyEnum { A, #[enum_tools(rename = "B*")] B, C }
128///
129/// assert_eq!(MyEnum::B.as_str(), "B*");
130/// assert_eq!(MyEnum::A.next(), MyEnum::C.pred());
131/// ```
132///
133/// # Intro
134///
135/// Many features have different modes (built in or to chose) based on the type of the
136/// enum: whether it has holes or not.
137///
138/// Examples for "gapless" (i.e. without holes)
139/// - `enum Name { A, B, C }`
140/// - `enum Name { A=5, B=7, C=6 }`
141///
142/// Examples for "with holes"
143/// - `enum Name { A=0, B=1, C=9 }`
144/// - `enum Name { A=0, B=5, C=1 }`
145///
146/// The modes "with holes" are not as performant and/or small as the "gapless" version,
147/// but functionally identical.
148///
149/// Also please note that all functions which have an order do order based on values
150/// (and not the order of there occurrence, and thus identical to derived `Ord` and `PartialOrd`).
151///
152/// # Common parameter
153///
154/// All features which create a function (or constant) have the two optional parameters:
155/// - `name`: the name of the function (the default is shown in the description below)
156/// - `vis`: the visibility, either `""`, `"pub(crate)"` or `"pub"`, defaults to the same as the enum itself
157///
158/// # Dependencies
159///
160/// Some features depend on others. When the other feature is not enabled by the user then it will
161/// be automatically enabled. The `vis` is `""` (inherent/private), the `name` starts with two underscores
162/// (this may change at any time and is not considered a breaking change as no other code should depend on it).
163///
164/// # Auto modes
165///
166/// Some features have an `"auto"` mode (which is always default).
167/// What implementation is chosen by `"auto"` may change at any time, this will not change the
168/// function but probably the size or speed of the code. This is not considered a breaking change.
169///
170/// If you want a specific mode, specify it.
171///
172/// # Compile time features
173///
174/// ## sorted
175///
176/// Ensure that the enum is sorted by name and/or value.
177///
178/// It is a compile-error when the enum is not in order.
179///
180/// Parameter:
181/// - `name` (optional)
182/// - `value` (optional)
183///
184/// Example:
185/// `#[enum_tools(sorted(name, value))]`
186///
187/// # Value attributes
188///
189/// The only supported value attribute is `rename` (see example above).
190/// This will affect all from/to str(ing) functions including the `names` iterator.
191///
192/// # Function/Constant Features
193///
194/// The name of the function is identical to the feature, but can be changed.
195///
196/// ## as_str
197///
198/// `$vis fn as_str(self) -> &'static str {..}`
199///
200/// Parameter:
201/// - `mode`:
202/// - `"auto"`: (default)
203/// - `"match"`: match statement
204/// - `"table"`: a table with all names, will be shared with `FromStr`, `from_str` and `names` if they also use a table.
205/// - `name`, `vis`: see common parameter
206///
207/// ## from_str
208///
209/// `$vis fn from_str(s: &str) -> Option<Self> {..}`
210///
211/// Parameter:
212/// - `mode`:
213/// - `"auto"`: (default)
214/// - `"match"`: match statement
215/// - `"table"`: a table with all names, will be shared with `as_str`, `FromStr` and `names` if they also use a table.
216/// And possibly a second table with all values if the enum has holes, will be shared with `FromStr` and `iter` if they also use a table.
217/// - `name`, `vis`: see common parameter
218///
219/// ## into
220///
221/// `$vis const fn into(self) -> $repr`
222///
223/// Converts the enum into the primitive.
224///
225/// ## MAX
226///
227/// `$vis const MAX : Self = ..`
228///
229/// The enum with the maximum value.
230///
231/// ## MIN
232///
233/// `$vis const MIN : Self = ..`
234///
235/// The enum with the minimum value.
236///
237/// ## next
238///
239/// `$vis fn next(self) -> Option<Self> {..}`
240///
241/// The next value after this, or `None` if `Self::MAX`.
242///
243/// ## next_back
244///
245/// `$vis fn next_back(self) -> Option<Self> {..}`
246///
247/// The value before this, or `None` if `Self::MIN`.
248///
249/// ## try_from
250///
251/// `$vis fn try_from(value: $repr) -> Option<Self>`
252///
253/// Converts the primitive type into the enum.
254///
255/// # Trait Features
256///
257/// The features are similar to the function above and/or the trait.
258///
259/// The error type is always `()` since this crate can't create one and it's not known
260/// if the user does provide one or one should be created.
261/// In the future there may be options to fine tune this.
262///
263/// ## Debug
264///
265/// Implement [`Debug`](core::fmt::Debug) by calling `as_str` (see above).
266///
267/// ## Display
268///
269/// Implement [`Display`](core::fmt::Display) by calling `as_str` (see above).
270///
271/// ## FromStr
272///
273/// Implement [`FromStr`](core::str::FromStr).
274///
275/// Parameter:
276/// - `mode`:
277/// - `"auto"`: (default)
278/// - `"match"`: match statement
279/// - `"table"`: a table with all names, will be shared with `as_str`, `from_str` and `names` if they also use a table.
280/// And possibly a second table with all values if the enum has holes, will be shared with `from_str` and `iter` if they also use a table.
281///
282/// The error is `()`.
283///
284/// ## Into
285///
286/// Implement `From<Self> for $repr`.
287///
288/// Converts the enum into the primitive.
289///
290/// ## IntoStr
291///
292/// Implement `From<Self> for &'static str`.
293///
294/// Converts the enum into a str by calling `as_str` (see above).
295///
296/// ## TryFrom
297///
298/// Implement `TryFrom<$repr> for Self`.
299///
300/// Converts the primitive type into the enum.
301///
302/// Error: `()`
303///
304/// # Iterator Features
305///
306/// These features implement a function for the enum and a struct for the iterator.
307///
308/// Example:
309/// ```
310/// # use enum_tools::EnumTools;
311/// #[derive(Clone, Copy, EnumTools, PartialEq)]
312/// #[enum_tools(iter, Debug)]
313/// #[repr(usize)]
314/// pub enum MyEnum { A, B, C }
315///
316/// let mut it : MyEnumIter = MyEnum::iter();
317/// assert_eq!(it.next(), Some(MyEnum::A));
318/// assert_eq!(it.next_back(), Some(MyEnum::C));
319/// assert_eq!(it.next(), Some(MyEnum::B));
320/// assert_eq!(it.next_back(), None);
321/// assert_eq!(it.next(), None);
322/// ```
323///
324/// ## iter
325///
326/// `$vis fn iter() -> SelfIter {..}`
327///
328/// An iterator over the values of the enum (in value order).
329///
330/// The struct implements: [`Iterator`](::core::iter::Iterator), [`DoubleEndedIterator`](::core::iter::DoubleEndedIterator), [`ExactSizeIterator`](::core::iter::ExactSizeIterator), [`FusedIterator`](::core::iter::FusedIterator).
331///
332/// Parameter:
333/// - `struct_name`: name of the generated struct, defaults to the name of the enum and 'Iter'.
334/// - `mode`:
335/// - `"auto"`: (default) will pick range for gapless and something appropriate otherwise
336/// - `"range"`: only available on gapless enums, simply iterate over a range
337/// with a conversion (as a no-op) to the enum-value
338/// - `"next_and_back"`: use `next` and `next_back` (see below) for the iteration
339/// - `"match"`: match statement
340/// - `"table"`: a table with all enums, will be shared with `FromStr` and `from_str` if they use a table.
341/// - `"table_inline"`: use an array (not a reference) of all enums to generate an iterator.
342/// Similar to `[Self::A, Self::B, ...].into_iter()`.
343/// Since the table is loaded every time into ram this is only a good pick for enums with few values.
344/// - `name`, `vis`: see common parameter
345///
346/// ## names
347///
348/// `$vis fn names() -> SelfNames {..}`
349///
350/// An iterator over the names of the enum (in value order).
351///
352/// The struct implements: [`Iterator`](::core::iter::Iterator), [`DoubleEndedIterator`](::core::iter::DoubleEndedIterator), [`ExactSizeIterator`](::core::iter::ExactSizeIterator), [`FusedIterator`](::core::iter::FusedIterator).
353///
354/// Parameter:
355/// - `struct_name`: name of the generated struct, defaults to the name of the enum and 'Names'.
356/// - `name`, `vis`: see common parameter
357///
358/// This always generates a table, will be shared with `as_str`, `FromStr` and `from_str` if they also use a table.
359///
360/// ## range
361///
362/// `$vis fn range(start: Self, end: Self) -> SelfIter {..}`
363///
364/// An Iterator over a inclusive range of the enum, in value order, similar to `..=`.
365///
366/// This feature requires that the feature `iter` is activated and the mode `"table_inline"` is not used.
367///
368/// For enums with holes the function to create the range may be not very performant.
369///
370#[proc_macro_error]
371#[proc_macro_derive(EnumTools, attributes(enum_tools))]
372pub fn enum_tools(tokens: proc_macro::TokenStream) -> proc_macro::TokenStream {
373 let input = parse_macro_input!(tokens as DeriveInput);
374 let (derive, features) = generator::Derive::parse(input);
375
376 derive.generate(features).into()
377}