strum_macros/lib.rs
1//! # Strum
2//!
3//! Strum is a set of macros and traits for working with
4//! enums and strings easier in Rust.
5//!
6//! This crate only contains derive macros for use with the
7//! [`strum`](https://docs.rs/strum)
8//! crate. The macros provied by this crate are also available by
9//! enabling the `derive` feature in aforementioned `strum` crate.
10
11#![recursion_limit = "128"]
12
13extern crate proc_macro;
14
15mod helpers;
16mod macros;
17
18use proc_macro2::TokenStream;
19use std::env;
20use syn::DeriveInput;
21
22fn debug_print_generated(ast: &DeriveInput, toks: &TokenStream) {
23 let debug = env::var("STRUM_DEBUG");
24 if let Ok(s) = debug {
25 if s == "1" {
26 println!("{}", toks);
27 }
28
29 if ast.ident == s {
30 println!("{}", toks);
31 }
32 }
33}
34
35/// Converts strings to enum variants based on their name.
36///
37/// auto-derives `std::str::FromStr` on the enum. Each variant of the enum will match on its own
38/// name. This can be overridden using `serialize="DifferentName"` or `to_string="DifferentName"`
39/// on the attribute as shown below.
40/// Multiple deserializations can be added to the same variant. If the variant contains additional
41/// data, they will be set to their default values upon deserialization.
42///
43/// The `default` attribute can be applied to a tuple variant with a single data parameter. When a
44/// match isn't found, the given variant will be returned and the input string will be captured in
45/// the parameter.
46///
47/// Note that the implementation of `FromStr` by default only matches on the name of the
48/// variant. There is an option to match on different case conversions through the
49/// `#[strum(serialize_all = "snake_case")]` type attribute.
50///
51/// See the [Additional Attributes](https://docs.rs/strum/latest/strum/additional_attributes/index.html)
52/// Section for more information on using this feature.
53///
54/// If you have a large enum, you may want to consider using the `use_phf` attribute here.
55/// PHF (Perfect Hash Functions) use a hash lookup instead of a linear search that may perform faster
56/// for large enums. Note: as with all optimizations, you should test this for your specific usecase
57/// rather than just assume it will be faster. With SIMD + pipelining, linear string search (aka memcmp)
58/// can be very fast for enums with a surprisingly large number of enum variants.
59///
60/// # Infallible Parsing
61///
62/// If the enum has a `#[strum(default)]` variant and no `parse_err_ty` is set, parsing is
63/// infallible: `From<&str>` is derived instead of `TryFrom<&str>`, which allows calling
64/// `MyEnum::from("string")` directly.
65///
66/// # Custom Error Types
67///
68/// The default error type is `strum::ParseError`. This can be overridden by applying both the
69/// `parse_err_ty` and `parse_err_fn` attributes at the type level. `parse_err_fn` should be a
70/// function that accepts an `&str` and returns the type `parse_err_ty`. See [this test
71/// case](https://github.com/Peternator7/strum/blob/9db3c4dc9b6f585aeb9f5f15f9cc18b6cf4fd780/strum_tests/tests/from_str.rs#L233)
72/// for an example. When `parse_err_ty` is set, `TryFrom<&str>` is always derived, even if the
73/// enum has a `#[strum(default)]` variant.
74///
75/// # Example how to use `EnumString`
76/// ```
77/// use std::str::FromStr;
78/// use strum_macros::EnumString;
79///
80/// #[derive(Debug, PartialEq, EnumString)]
81/// enum Color {
82/// Red,
83/// // The Default value will be inserted into range if we match "Green".
84/// Green {
85/// range: usize,
86/// },
87///
88/// // We can match on multiple different patterns.
89/// #[strum(serialize = "blue", serialize = "b")]
90/// Blue(usize),
91///
92/// // Notice that we can disable certain variants from being found
93/// #[strum(disabled)]
94/// Yellow,
95///
96/// // We can make the comparison case insensitive (however Unicode is not supported at the moment)
97/// #[strum(ascii_case_insensitive)]
98/// Black,
99/// }
100///
101/// /*
102/// //The generated code will look like:
103/// impl std::str::FromStr for Color {
104/// type Err = ::strum::ParseError;
105///
106/// fn from_str(s: &str) -> ::core::result::Result<Color, Self::Err> {
107/// match s {
108/// "Red" => ::core::result::Result::Ok(Color::Red),
109/// "Green" => ::core::result::Result::Ok(Color::Green { range:Default::default() }),
110/// "blue" => ::core::result::Result::Ok(Color::Blue(Default::default())),
111/// "b" => ::core::result::Result::Ok(Color::Blue(Default::default())),
112/// s if s.eq_ignore_ascii_case("Black") => ::core::result::Result::Ok(Color::Black),
113/// _ => ::core::result::Result::Err(::strum::ParseError::VariantNotFound),
114/// }
115/// }
116/// }
117/// */
118///
119/// // simple from string
120/// let color_variant = Color::from_str("Red").unwrap();
121/// assert_eq!(Color::Red, color_variant);
122/// // short version works too
123/// let color_variant = Color::from_str("b").unwrap();
124/// assert_eq!(Color::Blue(0), color_variant);
125/// // was disabled for parsing = returns parse-error
126/// let color_variant = Color::from_str("Yellow");
127/// assert!(color_variant.is_err());
128/// // however the variant is still normally usable
129/// println!("{:?}", Color::Yellow);
130/// let color_variant = Color::from_str("bLACk").unwrap();
131/// assert_eq!(Color::Black, color_variant);
132/// ```
133#[proc_macro_derive(EnumString, attributes(strum))]
134pub fn from_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
135 let ast = syn::parse_macro_input!(input as DeriveInput);
136
137 let toks =
138 macros::from_string::from_string_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
139 debug_print_generated(&ast, &toks);
140 toks.into()
141}
142
143/// Converts enum variants to `&'a str`, where `'a` is the lifetime of the input enum reference.
144///
145/// Implements `AsRef<str>` on your enum using the same rules as
146/// `Display` for determining what string is returned. The difference is that `as_ref()` returns
147/// a `&str` instead of a `String` so you don't allocate any additional memory with each call.
148///
149/// If you require a `&'static str`, you can use
150/// [`strum::IntoStaticStr`](crate::IntoStaticStr) instead.
151///
152/// ```
153/// // You need to bring the AsRef trait into scope to use it
154/// use std::convert::AsRef;
155/// use strum_macros::AsRefStr;
156///
157/// #[derive(AsRefStr, Debug)]
158/// enum Color {
159/// #[strum(serialize = "redred")]
160/// Red,
161/// Green {
162/// range: usize,
163/// },
164/// Blue(usize),
165/// Yellow,
166/// }
167///
168/// // uses the serialize string for Display
169/// let red = Color::Red;
170/// assert_eq!("redred", red.as_ref());
171/// // by default the variants Name
172/// let yellow = Color::Yellow;
173/// assert_eq!("Yellow", yellow.as_ref());
174/// // or for string formatting
175/// assert_eq!(
176/// "blue: Blue green: Green",
177/// format!(
178/// "blue: {} green: {}",
179/// Color::Blue(10).as_ref(),
180/// Color::Green { range: 42 }.as_ref()
181/// )
182/// );
183///
184/// // With prefix on all variants
185/// #[derive(AsRefStr, Debug)]
186/// #[strum(prefix = "/")]
187/// enum ColorWithPrefix {
188/// #[strum(serialize = "redred")]
189/// Red,
190/// Green,
191/// }
192///
193/// assert_eq!("/redred", ColorWithPrefix::Red.as_ref());
194/// assert_eq!("/Green", ColorWithPrefix::Green.as_ref());
195///
196/// // With suffix on all variants
197/// #[derive(AsRefStr, Debug)]
198/// #[strum(suffix = ".rs")]
199/// enum ColorWithSuffix {
200/// #[strum(serialize = "redred")]
201/// Red,
202/// Green,
203/// }
204///
205/// assert_eq!("redred.rs", ColorWithSuffix::Red.as_ref());
206/// assert_eq!("Green.rs", ColorWithSuffix::Green.as_ref());
207/// ```
208#[proc_macro_derive(AsRefStr, attributes(strum))]
209pub fn as_ref_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
210 let ast = syn::parse_macro_input!(input as DeriveInput);
211
212 let toks =
213 macros::as_ref_str::as_ref_str_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
214 debug_print_generated(&ast, &toks);
215 toks.into()
216}
217
218/// Implements `Strum::VariantNames` which adds an associated constant `VARIANTS` which is a `'static` slice of discriminant names.
219///
220/// Adds an `impl` block for the `enum` that adds a static `VARIANTS` array of `&'static str` that are the discriminant names.
221/// This will respect the `serialize_all` attribute on the `enum` (like `#[strum(serialize_all = "snake_case")]`.
222///
223/// ```
224/// // import the macros needed
225/// use strum_macros::{EnumString};
226/// // You need to import the trait, to have access to VARIANTS
227/// use strum::VariantNames;
228///
229/// #[derive(Debug, EnumString, strum_macros::VariantNames)]
230/// #[strum(serialize_all = "kebab-case")]
231/// enum Color {
232/// Red,
233/// Blue,
234/// Yellow,
235/// RebeccaPurple,
236/// }
237/// assert_eq!(["red", "blue", "yellow", "rebecca-purple"], Color::VARIANTS);
238/// ```
239#[proc_macro_derive(VariantNames, attributes(strum))]
240pub fn variant_names(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
241 let ast = syn::parse_macro_input!(input as DeriveInput);
242
243 let toks = macros::enum_variant_names::enum_variant_names_inner(&ast)
244 .unwrap_or_else(|err| err.to_compile_error());
245 debug_print_generated(&ast, &toks);
246 toks.into()
247}
248
249#[doc(hidden)]
250#[proc_macro_derive(EnumVariantNames, attributes(strum))]
251#[deprecated(
252 since = "0.26.0",
253 note = "please use `#[derive(VariantNames)]` instead"
254)]
255pub fn variant_names_deprecated(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
256 let ast = syn::parse_macro_input!(input as DeriveInput);
257
258 let toks = macros::enum_variant_names::enum_variant_names_inner(&ast)
259 .unwrap_or_else(|err| err.to_compile_error());
260 debug_print_generated(&ast, &toks);
261 toks.into()
262}
263
264/// Adds a `'static` slice with all of the Enum's variants.
265///
266/// Implements `strum::VariantArray` which adds an associated constant `VARIANTS`.
267/// This constant contains an array with all the variants of the enumerator.
268///
269/// This trait can only be autoderived if the enumerator is composed only of unit-type variants,
270/// meaning that the variants must not have any data.
271///
272/// ```
273/// use strum::VariantArray as _;
274/// use strum_macros::VariantArray;
275///
276/// #[derive(VariantArray, Debug, PartialEq, Eq)]
277/// enum Op {
278/// Add,
279/// Sub,
280/// Mul,
281/// Div,
282/// }
283///
284/// assert_eq!(Op::VARIANTS, &[Op::Add, Op::Sub, Op::Mul, Op::Div]);
285/// ```
286#[proc_macro_derive(VariantArray, attributes(strum))]
287pub fn static_variants_array(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
288 let ast = syn::parse_macro_input!(input as DeriveInput);
289
290 let toks = macros::enum_variant_array::static_variants_array_inner(&ast)
291 .unwrap_or_else(|err| err.to_compile_error());
292 debug_print_generated(&ast, &toks);
293 toks.into()
294}
295
296#[proc_macro_derive(AsStaticStr, attributes(strum))]
297#[doc(hidden)]
298#[deprecated(
299 since = "0.22.0",
300 note = "please use `#[derive(IntoStaticStr)]` instead"
301)]
302pub fn as_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
303 let ast = syn::parse_macro_input!(input as DeriveInput);
304
305 let toks = macros::as_ref_str::as_static_str_inner(
306 &ast,
307 ¯os::as_ref_str::GenerateTraitVariant::AsStaticStr,
308 )
309 .unwrap_or_else(|err| err.to_compile_error());
310 debug_print_generated(&ast, &toks);
311 toks.into()
312}
313
314/// Implements `From<MyEnum> for &'static str` on an enum.
315///
316/// Implements `From<YourEnum>` and `From<&'a YourEnum>` for `&'static str`. This is
317/// useful for turning an enum variant into a static string.
318/// The Rust `std` provides a blanket impl of the reverse direction - i.e. `impl Into<&'static str> for YourEnum`.
319///
320/// ```
321/// use strum_macros::IntoStaticStr;
322///
323/// #[derive(IntoStaticStr)]
324/// enum State<'a> {
325/// Initial(&'a str),
326/// Finished,
327/// }
328///
329/// fn verify_state<'a>(s: &'a str) {
330/// let mut state = State::Initial(s);
331/// // The following won't work because the lifetime is incorrect:
332/// // let wrong: &'static str = state.as_ref();
333/// // using the trait implemented by the derive works however:
334/// let right: &'static str = state.into();
335/// assert_eq!("Initial", right);
336/// state = State::Finished;
337/// let done: &'static str = state.into();
338/// assert_eq!("Finished", done);
339/// }
340///
341/// verify_state(&"hello world".to_string());
342/// ```
343#[proc_macro_derive(IntoStaticStr, attributes(strum))]
344pub fn into_static_str(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
345 let ast = syn::parse_macro_input!(input as DeriveInput);
346
347 let toks = macros::as_ref_str::as_static_str_inner(
348 &ast,
349 ¯os::as_ref_str::GenerateTraitVariant::From,
350 )
351 .unwrap_or_else(|err| err.to_compile_error());
352 debug_print_generated(&ast, &toks);
353 toks.into()
354}
355
356/// implements `std::string::ToString` on an enum
357///
358/// ```
359/// // You need to bring the ToString trait into scope to use it
360/// use std::string::ToString;
361/// use strum_macros;
362///
363/// #[derive(strum_macros::ToString, Debug)]
364/// enum Color {
365/// #[strum(serialize = "redred")]
366/// Red,
367/// Green {
368/// range: usize,
369/// },
370/// Blue(usize),
371/// Yellow,
372/// }
373///
374/// // uses the serialize string for Display
375/// let red = Color::Red;
376/// assert_eq!(String::from("redred"), red.to_string());
377/// // by default the variants Name
378/// let yellow = Color::Yellow;
379/// assert_eq!(String::from("Yellow"), yellow.to_string());
380/// ```
381#[deprecated(
382 since = "0.22.0",
383 note = "please use `#[derive(Display)]` instead. See issue https://github.com/Peternator7/strum/issues/132"
384)]
385#[doc(hidden)]
386#[proc_macro_derive(ToString, attributes(strum))]
387pub fn to_string(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
388 let ast = syn::parse_macro_input!(input as DeriveInput);
389
390 let toks =
391 macros::to_string::to_string_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
392 debug_print_generated(&ast, &toks);
393 toks.into()
394}
395
396/// Converts enum variants to strings.
397///
398/// Deriving `Display` on an enum prints out the given enum. This enables you to perform round
399/// trip style conversions from enum into string and back again for unit style variants. `Display`
400/// choose which serialization to used based on the following criteria:
401///
402/// 1. If there is a `to_string` property, this value will be used. There can only be one per variant.
403/// 2. Of the various `serialize` properties, the value with the longest length is chosen. If that
404/// behavior isn't desired, you should use `to_string`.
405/// 3. The name of the variant will be used if there are no `serialize` or `to_string` attributes.
406/// 4. If the enum has a `strum(prefix = "some_value_")`, every variant will have that prefix prepended
407/// to the serialization.
408/// 5. If the enum has a `strum(suffix = "_another_value")`, every variant will have that suffix appended
409/// to the serialization.
410/// 6. Enums with fields support string interpolation.
411/// Note this means the variant will not "round trip" if you then deserialize the string.
412///
413/// ```rust
414/// #[derive(strum_macros::Display)]
415/// pub enum Color {
416/// #[strum(to_string = "saturation is {sat}")]
417/// Red { sat: usize },
418/// #[strum(to_string = "hue is {1}, saturation is {0}")]
419/// Blue(usize, usize),
420/// }
421/// ```
422///
423/// ```
424/// // You need to bring the ToString trait into scope to use it
425/// use std::string::ToString;
426/// use strum_macros::Display;
427///
428/// #[derive(Display, Debug)]
429/// enum Color {
430/// #[strum(serialize = "redred")]
431/// Red,
432/// Green {
433/// range: usize,
434/// },
435/// Blue(usize),
436/// Yellow,
437/// #[strum(to_string = "purple with {sat} saturation")]
438/// Purple {
439/// sat: usize,
440/// },
441/// }
442///
443/// // uses the serialize string for Display
444/// let red = Color::Red;
445/// assert_eq!(String::from("redred"), format!("{}", red));
446/// // by default the variants Name
447/// let yellow = Color::Yellow;
448/// assert_eq!(String::from("Yellow"), yellow.to_string());
449/// // or for string formatting
450/// assert_eq!(
451/// "blue: Blue green: Green",
452/// format!(
453/// "blue: {} green: {}",
454/// Color::Blue(10),
455/// Color::Green { range: 42 }
456/// )
457/// );
458/// // you can also use named fields in message
459/// let purple = Color::Purple { sat: 10 };
460/// assert_eq!(String::from("purple with 10 saturation"), purple.to_string());
461/// ```
462#[proc_macro_derive(Display, attributes(strum))]
463pub fn display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
464 let ast = syn::parse_macro_input!(input as DeriveInput);
465
466 let toks = macros::display::display_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
467 debug_print_generated(&ast, &toks);
468 toks.into()
469}
470
471/// Creates a new type that iterates over the variants of an enum.
472///
473/// Iterate over the variants of an Enum. Any additional data on your variants will be set to `Default::default()`.
474/// The macro implements [`strum::IntoEnumIterator`](https://docs.rs/strum/latest/strum/trait.IntoEnumIterator.html) on your enum and creates a new type called `YourEnumIter` that is the iterator object.
475/// You cannot derive `EnumIter` on any type with a lifetime bound (`<'a>`) because the iterator would surely
476/// create [unbounded lifetimes](https://doc.rust-lang.org/nightly/nomicon/unbounded-lifetimes.html).
477///
478/// ```
479///
480/// // You need to bring the trait into scope to use it!
481/// use strum::IntoEnumIterator;
482/// use strum_macros::EnumIter;
483///
484/// #[derive(EnumIter, Debug, PartialEq)]
485/// enum Color {
486/// Red,
487/// Green { range: usize },
488/// Blue(usize),
489/// Yellow,
490/// }
491///
492/// // It's simple to iterate over the variants of an enum.
493/// for color in Color::iter() {
494/// println!("My favorite color is {:?}", color);
495/// }
496///
497/// let mut ci = Color::iter();
498/// assert_eq!(Some(Color::Red), ci.next());
499/// assert_eq!(Some(Color::Green {range: 0}), ci.next());
500/// assert_eq!(Some(Color::Blue(0)), ci.next());
501/// assert_eq!(Some(Color::Yellow), ci.next());
502/// assert_eq!(None, ci.next());
503/// ```
504#[proc_macro_derive(EnumIter, attributes(strum))]
505pub fn enum_iter(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
506 let ast = syn::parse_macro_input!(input as DeriveInput);
507
508 let toks =
509 macros::enum_iter::enum_iter_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
510 debug_print_generated(&ast, &toks);
511 toks.into()
512}
513
514/// Generated `is_*()` methods for each variant.
515/// E.g. `Color.is_red()`.
516///
517/// ```
518///
519/// use strum_macros::EnumIs;
520///
521/// #[derive(EnumIs, Debug)]
522/// enum Color {
523/// Red,
524/// Green { range: usize },
525/// }
526///
527/// assert!(Color::Red.is_red());
528/// assert!(Color::Green{range: 0}.is_green());
529/// ```
530#[proc_macro_derive(EnumIs, attributes(strum))]
531pub fn enum_is(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
532 let ast = syn::parse_macro_input!(input as DeriveInput);
533
534 let toks = macros::enum_is::enum_is_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
535 debug_print_generated(&ast, &toks);
536 toks.into()
537}
538
539/// Generated `try_as_*()` methods for all tuple-style variants.
540/// E.g. `Message.try_as_write()`.
541///
542/// These methods will only be generated for tuple-style variants, not for named or unit variants.
543///
544/// ```
545/// use strum_macros::EnumTryAs;
546///
547/// #[derive(EnumTryAs, Debug)]
548/// enum Message {
549/// Quit,
550/// Move { x: i32, y: i32 },
551/// Write(String),
552/// ChangeColor(i32, i32, i32),
553/// }
554///
555/// assert_eq!(
556/// Message::Write(String::from("Hello")).try_as_write(),
557/// Some(String::from("Hello"))
558/// );
559/// assert_eq!(
560/// Message::ChangeColor(1, 2, 3).try_as_change_color(),
561/// Some((1, 2, 3))
562/// );
563/// ```
564#[proc_macro_derive(EnumTryAs, attributes(strum))]
565pub fn enum_try_as(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
566 let ast = syn::parse_macro_input!(input as DeriveInput);
567
568 let toks =
569 macros::enum_try_as::enum_try_as_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
570 debug_print_generated(&ast, &toks);
571 toks.into()
572}
573
574/// Creates a new type that maps all the variants of an enum to another generic value.
575///
576/// This macro only supports enums with unit type variants.A new type called `YourEnumTable<T>`. Essentially, it's a wrapper
577/// `[T; YourEnum::Count]` where gets/sets are infallible. Some important caveats to note:
578///
579/// * The size of `YourEnumTable<T>` increases with the number of variants, not the number of values because it's always
580/// fully populated. This means it may not be a good choice for sparsely populated maps.
581///
582/// * Lookups are basically constant time since it's functionally an array index.
583///
584/// * Your variants cannot have associated data. You can use `EnumDiscriminants` to generate an Enum with the same
585/// names to work around this.
586///
587/// # Stability
588///
589/// Several people expressed interest in a data structure like this and pushed the PR through to completion, but the api
590/// seems incomplete, and I reserve the right to deprecate it in the future if it becomes clear the design is flawed.
591///
592/// # Example
593/// ```rust
594/// use strum_macros::EnumTable;
595///
596/// #[derive(EnumTable)]
597/// enum Color {
598/// Red,
599/// Yellow,
600/// Green,
601/// Blue,
602/// }
603///
604/// assert_eq!(ColorTable::default(), ColorTable::new(0, 0, 0, 0));
605/// assert_eq!(ColorTable::filled(2), ColorTable::new(2, 2, 2, 2));
606/// assert_eq!(ColorTable::from_closure(|_| 3), ColorTable::new(3, 3, 3, 3));
607/// assert_eq!(ColorTable::default().transform(|_, val| val + 2), ColorTable::new(2, 2, 2, 2));
608///
609/// let mut complex_map = ColorTable::from_closure(|color| match color {
610/// Color::Red => 0,
611/// _ => 3
612/// });
613///
614/// complex_map[Color::Green] = complex_map[Color::Red];
615/// assert_eq!(complex_map, ColorTable::new(0, 3, 0, 3));
616/// ```
617#[doc(hidden)]
618#[proc_macro_derive(EnumTable, attributes(strum))]
619pub fn enum_table(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
620 let ast = syn::parse_macro_input!(input as DeriveInput);
621
622 let toks =
623 macros::enum_table::enum_table_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
624 debug_print_generated(&ast, &toks);
625 toks.into()
626}
627
628/// Add a function to enum that allows accessing variants by its discriminant
629///
630/// This macro adds a standalone function to obtain an enum variant by its discriminant. The macro adds
631/// `from_repr(discriminant: usize) -> Option<YourEnum>` as a standalone function on the enum. For
632/// variants with additional data, the returned variant will use the `Default` trait to fill the
633/// data. The discriminant follows the same rules as `rustc`. The first discriminant is zero and each
634/// successive variant has a discriminant of one greater than the previous variant, except where an
635/// explicit discriminant is specified. The type of the discriminant will match the `repr` type if
636/// it is specified.
637///
638/// When the macro is applied using rustc >= 1.46 and when there is no additional data on any of
639/// the variants, the `from_repr` function is marked `const`. rustc >= 1.46 is required
640/// to allow `match` statements in `const fn`. The no additional data requirement is due to the
641/// inability to use `Default::default()` in a `const fn`.
642///
643/// You cannot derive `FromRepr` on any type with a lifetime bound (`<'a>`) because the function would surely
644/// create [unbounded lifetimes](https://doc.rust-lang.org/nightly/nomicon/unbounded-lifetimes.html).
645///
646/// ```
647///
648/// use strum_macros::FromRepr;
649///
650/// #[derive(FromRepr, Debug, PartialEq)]
651/// enum Color {
652/// Red,
653/// Green { range: usize },
654/// Blue(usize),
655/// Yellow,
656/// }
657///
658/// assert_eq!(Some(Color::Red), Color::from_repr(0));
659/// assert_eq!(Some(Color::Green {range: 0}), Color::from_repr(1));
660/// assert_eq!(Some(Color::Blue(0)), Color::from_repr(2));
661/// assert_eq!(Some(Color::Yellow), Color::from_repr(3));
662/// assert_eq!(None, Color::from_repr(4));
663///
664/// // Custom discriminant tests
665/// #[derive(FromRepr, Debug, PartialEq)]
666/// #[repr(u8)]
667/// enum Vehicle {
668/// Car = 1,
669/// Truck = 3,
670/// }
671///
672/// assert_eq!(None, Vehicle::from_repr(0));
673/// ```
674///
675/// On versions of rust >= 1.46, the `from_repr` function is marked `const`.
676///
677/// ```rust
678/// use strum_macros::FromRepr;
679///
680/// #[derive(FromRepr, Debug, PartialEq)]
681/// #[repr(u8)]
682/// enum Number {
683/// One = 1,
684/// Three = 3,
685/// }
686///
687/// const fn number_from_repr(d: u8) -> Option<Number> {
688/// Number::from_repr(d)
689/// }
690///
691/// assert_eq!(None, number_from_repr(0));
692/// assert_eq!(Some(Number::One), number_from_repr(1));
693/// assert_eq!(None, number_from_repr(2));
694/// assert_eq!(Some(Number::Three), number_from_repr(3));
695/// assert_eq!(None, number_from_repr(4));
696/// ```
697#[proc_macro_derive(FromRepr, attributes(strum))]
698pub fn from_repr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
699 let ast = syn::parse_macro_input!(input as DeriveInput);
700
701 let toks =
702 macros::from_repr::from_repr_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
703 debug_print_generated(&ast, &toks);
704 toks.into()
705}
706
707/// Add a verbose message to an enum variant.
708///
709/// Encode strings into the enum itself. The `strum_macros::EmumMessage` macro implements the `strum::EnumMessage` trait.
710/// `EnumMessage` looks for `#[strum(message="...")]` attributes on your variants.
711/// You can also provided a `detailed_message="..."` attribute to create a separate more detailed message than the first.
712///
713/// `EnumMessage` also exposes the variants doc comments through `get_documentation()`. This is useful in some scenarios,
714/// but `get_message` should generally be preferred. Rust doc comments are intended for developer facing documentation,
715/// not end user messaging.
716///
717/// ```
718/// // You need to bring the trait into scope to use it
719/// use strum::EnumMessage;
720/// use strum_macros;
721///
722/// #[derive(strum_macros::EnumMessage, Debug)]
723/// #[allow(dead_code)]
724/// enum Color {
725/// /// Danger color.
726/// #[strum(message = "Red", detailed_message = "This is very red")]
727/// Red,
728/// #[strum(message = "Simply Green")]
729/// Green { range: usize },
730/// #[strum(serialize = "b", serialize = "blue")]
731/// Blue(usize),
732/// }
733///
734/// // Generated code looks like more or less like this:
735/// /*
736/// impl ::strum::EnumMessage for Color {
737/// fn get_message(&self) -> ::core::option::Option<&'static str> {
738/// match self {
739/// &Color::Red => ::core::option::Option::Some("Red"),
740/// &Color::Green {..} => ::core::option::Option::Some("Simply Green"),
741/// _ => None
742/// }
743/// }
744///
745/// fn get_detailed_message(&self) -> ::core::option::Option<&'static str> {
746/// match self {
747/// &Color::Red => ::core::option::Option::Some("This is very red"),
748/// &Color::Green {..}=> ::core::option::Option::Some("Simply Green"),
749/// _ => None
750/// }
751/// }
752///
753/// fn get_documentation(&self) -> ::std::option::Option<&'static str> {
754/// match self {
755/// &Color::Red => ::std::option::Option::Some("Danger color."),
756/// _ => None
757/// }
758/// }
759///
760/// fn get_serializations(&self) -> &'static [&'static str] {
761/// match self {
762/// &Color::Red => {
763/// static ARR: [&'static str; 1] = ["Red"];
764/// &ARR
765/// },
766/// &Color::Green {..}=> {
767/// static ARR: [&'static str; 1] = ["Green"];
768/// &ARR
769/// },
770/// &Color::Blue (..) => {
771/// static ARR: [&'static str; 2] = ["b", "blue"];
772/// &ARR
773/// },
774/// }
775/// }
776/// }
777/// */
778///
779/// let c = Color::Red;
780/// assert_eq!("Red", c.get_message().unwrap());
781/// assert_eq!("This is very red", c.get_detailed_message().unwrap());
782/// assert_eq!("Danger color.", c.get_documentation().unwrap());
783/// assert_eq!(["Red"], c.get_serializations());
784/// ```
785#[proc_macro_derive(EnumMessage, attributes(strum))]
786pub fn enum_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
787 let ast = syn::parse_macro_input!(input as DeriveInput);
788
789 let toks = macros::enum_messages::enum_message_inner(&ast)
790 .unwrap_or_else(|err| err.to_compile_error());
791 debug_print_generated(&ast, &toks);
792 toks.into()
793}
794
795/// Add custom properties to enum variants.
796///
797/// Enables the encoding of arbitrary constants into enum variants. This method
798/// currently only supports adding additional string values. Other types of literals are still
799/// experimental in the rustc compiler. The generated code works by nesting match statements.
800/// The first match statement matches on the type of the enum, and the inner match statement
801/// matches on the name of the property requested. This design works well for enums with a small
802/// number of variants and properties, but scales linearly with the number of variants so may not
803/// be the best choice in all situations.
804///
805/// ```
806///
807/// use strum_macros;
808/// // bring the trait into scope
809/// use strum::EnumProperty;
810///
811/// #[derive(strum_macros::EnumProperty, Debug)]
812/// #[allow(dead_code)]
813/// enum Color {
814/// #[strum(props(Red = "255", Blue = "255", Green = "255"))]
815/// White,
816/// #[strum(props(Red = "0", Blue = "0", Green = "0"))]
817/// Black,
818/// #[strum(props(Red = "0", Blue = "255", Green = "0"))]
819/// Blue,
820/// #[strum(props(Red = "255", Blue = "0", Green = "0"))]
821/// Red,
822/// #[strum(props(Red = "0", Blue = "0", Green = "255"))]
823/// Green,
824/// }
825///
826/// let my_color = Color::Red;
827/// let display = format!(
828/// "My color is {:?}. It's RGB is {},{},{}",
829/// my_color,
830/// my_color.get_str("Red").unwrap(),
831/// my_color.get_str("Green").unwrap(),
832/// my_color.get_str("Blue").unwrap()
833/// );
834/// assert_eq!("My color is Red. It\'s RGB is 255,0,0", &display);
835/// ```
836#[proc_macro_derive(EnumProperty, attributes(strum))]
837pub fn enum_properties(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
838 let ast = syn::parse_macro_input!(input as DeriveInput);
839
840 let toks = macros::enum_properties::enum_properties_inner(&ast)
841 .unwrap_or_else(|err| err.to_compile_error());
842 debug_print_generated(&ast, &toks);
843 toks.into()
844}
845
846/// Generate a new type with only the discriminant names.
847///
848/// Given an enum named `MyEnum`, generates another enum called `MyEnumDiscriminants` with the same
849/// variants but without any data fields. This is useful when you wish to determine the variant of
850/// an `enum` but one or more of the variants contains a non-`Default` field. `From`
851/// implementations are generated so that you can easily convert from `MyEnum` to
852/// `MyEnumDiscriminants`.
853///
854/// By default, the generated enum has the following derives: `Clone, Copy, Debug, PartialEq, Eq`.
855/// If your enum derives `Default` and has a `#[default]` variant, that will also be copied onto
856/// the discriminant enum. You can add additional derives using the
857/// `#[strum_discriminants(derive(AdditionalDerive))]` attribute.
858///
859/// Note, the variant attributes passed to the discriminant enum are filtered to avoid compilation
860/// errors due to the derives mismatches, thus only `#[doc]`, `#[cfg]`, `#[allow]`, and `#[deny]`
861/// are passed through by default. If you want to specify a custom attribute on the discriminant
862/// variant, wrap it with `#[strum_discriminants(...)]` attribute.
863///
864/// ```
865/// // Bring trait into scope
866/// use std::str::FromStr;
867/// use strum::{IntoEnumIterator, EnumMessage as _};
868/// use strum_macros::{EnumDiscriminants, EnumIter, EnumString, EnumMessage};
869///
870/// #[derive(Debug)]
871/// struct NonDefault;
872///
873/// // simple example
874/// # #[allow(dead_code)]
875/// #[derive(Debug, EnumDiscriminants)]
876/// #[strum_discriminants(derive(EnumString, EnumMessage))]
877/// #[strum_discriminants(doc = "This is the docstring on the generated type.")]
878/// enum MyEnum {
879/// #[strum_discriminants(strum(message = "Variant zero"))]
880/// Variant0(NonDefault),
881/// Variant1 { a: NonDefault },
882/// }
883///
884/// // You can rename the generated enum using the `#[strum_discriminants(name(OtherName))]` attribute:
885/// # #[allow(dead_code)]
886/// #[derive(Debug, EnumDiscriminants)]
887/// #[strum_discriminants(derive(EnumIter))]
888/// #[strum_discriminants(name(MyVariants))]
889/// enum MyEnumR {
890/// Variant0(bool),
891/// Variant1 { a: bool },
892/// }
893///
894/// // test simple example
895/// assert_eq!(
896/// MyEnumDiscriminants::Variant0,
897/// MyEnumDiscriminants::from_str("Variant0").unwrap()
898/// );
899/// // test rename example combined with EnumIter
900/// assert_eq!(
901/// vec![MyVariants::Variant0, MyVariants::Variant1],
902/// MyVariants::iter().collect::<Vec<_>>()
903/// );
904///
905/// // Make use of the auto-From conversion to check whether an instance of `MyEnum` matches a
906/// // `MyEnumDiscriminants` discriminant.
907/// assert_eq!(
908/// MyEnumDiscriminants::Variant0,
909/// MyEnum::Variant0(NonDefault).into()
910/// );
911/// assert_eq!(
912/// MyEnumDiscriminants::Variant0,
913/// MyEnumDiscriminants::from(MyEnum::Variant0(NonDefault))
914/// );
915///
916/// // Make use of the EnumMessage on the `MyEnumDiscriminants` discriminant.
917/// assert_eq!(
918/// MyEnumDiscriminants::Variant0.get_message(),
919/// Some("Variant zero")
920/// );
921/// ```
922///
923/// It is also possible to specify the visibility (e.g. `pub`/`pub(crate)`/etc.)
924/// of the generated enum. By default, the generated enum inherits the
925/// visibility of the parent enum it was generated from.
926///
927/// ```
928/// use strum_macros::EnumDiscriminants;
929///
930/// // You can set the visibility of the generated enum using the `#[strum_discriminants(vis(..))]` attribute:
931/// mod inner {
932/// use strum_macros::EnumDiscriminants;
933///
934/// # #[allow(dead_code)]
935/// #[derive(Debug, EnumDiscriminants)]
936/// #[strum_discriminants(vis(pub))]
937/// #[strum_discriminants(name(PubDiscriminants))]
938/// enum PrivateEnum {
939/// Variant0(bool),
940/// Variant1 { a: bool },
941/// }
942/// }
943///
944/// // test visibility example, `PrivateEnum` should not be accessible here
945/// assert_ne!(
946/// inner::PubDiscriminants::Variant0,
947/// inner::PubDiscriminants::Variant1,
948/// );
949/// ```
950#[proc_macro_derive(EnumDiscriminants, attributes(strum, strum_discriminants))]
951pub fn enum_discriminants(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
952 let ast = syn::parse_macro_input!(input as DeriveInput);
953
954 let toks = macros::enum_discriminants::enum_discriminants_inner(&ast)
955 .unwrap_or_else(|err| err.to_compile_error());
956 debug_print_generated(&ast, &toks);
957 toks.into()
958}
959
960/// Add a constant `usize` equal to the number of variants.
961///
962/// For a given enum generates implementation of `strum::EnumCount`,
963/// which adds a static property `COUNT` of type usize that holds the number of variants.
964///
965/// ```
966/// use strum::{EnumCount, IntoEnumIterator};
967/// use strum_macros::{EnumCount as EnumCountMacro, EnumIter};
968///
969/// #[derive(Debug, EnumCountMacro, EnumIter)]
970/// enum Week {
971/// Sunday,
972/// Monday,
973/// Tuesday,
974/// Wednesday,
975/// Thursday,
976/// Friday,
977/// Saturday,
978/// }
979///
980/// assert_eq!(7, Week::COUNT);
981/// assert_eq!(Week::iter().count(), Week::COUNT);
982///
983/// ```
984#[proc_macro_derive(EnumCount, attributes(strum))]
985pub fn enum_count(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
986 let ast = syn::parse_macro_input!(input as DeriveInput);
987 let toks =
988 macros::enum_count::enum_count_inner(&ast).unwrap_or_else(|err| err.to_compile_error());
989 debug_print_generated(&ast, &toks);
990 toks.into()
991}