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