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