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