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