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