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