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