beerec_variants/
lib.rs

1mod ident;
2mod nested_meta;
3mod rename;
4mod string;
5mod target;
6
7use darling::FromDeriveInput;
8use proc_macro::TokenStream;
9use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
10use syn::DeriveInput;
11
12use self::target::r#enum::TargetEnum;
13
14/// The actual derive macro implementation.
15///
16/// This function is introduced in conjunction with `derive_enum_variants`
17/// because the `proc_macro_derive` attribute requires its target function
18/// signature to be `fn(proc_macro::TokenStream) -> proc_macro::TokenStream`.
19///
20/// This `impl` function allows to return a `syn::Result`, enabling the `?`
21/// operator for fallible function calls. This way, the macro's function
22/// can map the error value to a compilation error, providing more precise
23/// error messages.
24#[rustfmt::skip]
25#[allow(clippy::too_many_lines)]
26fn derive_enum_variants_impl(input: &DeriveInput) -> syn::Result<TokenStream2> {
27    let target_enum = TargetEnum::from_derive_input(input)?;
28
29    let enum_ident = target_enum.ident();
30
31    let variants_count = target_enum.variants_count();
32    let variants_idents = target_enum.iter_variant_idents();
33
34    let variants_as_str_match_branches = target_enum.iter_variant_as_str_match_branches();
35    let variants_as_str_abbr_match_branches = target_enum.iter_variant_as_str_abbr_match_branches();
36
37    let variants_list_string = target_enum.variants_list_string();
38    let variants_list_string_abbr = target_enum.variants_list_string_abbr();
39
40    let iterable_variants_doc = format!(
41        "The array of _iterable_ (i.e. non-skipped) [`{enum_ident}`] variants."
42    );
43
44    let iterable_variants_count_doc = format!(
45        "The number of _iterable_ (i.e. non-skipped) [`{enum_ident}`] variants."
46    );
47
48    let as_str_doc = format!(
49        r"Returns a string representation of the [`{enum_ident}`] variant.
50
51# Notes
52
53This method is generated by the [`Variants`] derive macro from the [`beerec-variants`]
54crate, it applies rename strategies following a priority-based fallback approach:
55
561. [`InnerRenameStrategy`] (_highest priority_) - uses the string
57   produced by the rename strategy from the `#[variants(rename(...))]`
58   attribute, if one has been specified for the variant;
591. [`OuterRenameStrategy`] (_fallback_) - uses the string produced by the
60   rename strategy from the `#[variants(rename(...))]` attribute, if one has
61   been specified for the type;
621. **No renaming** (_default_) - converts the variant identifier to a string
63   if neither the type-level nor the variant-level rename attribute has been
64   specified.
65
66[`beerec-variants`]: https://docs.rs/beerec-variants
67[`Variants`]: https://docs.rs/beerec-variants/latest/beerec_variants/derive.Variants.html"
68    );
69
70    let as_str_abbr_doc = format!(
71        r"Returns an abbreviated string representation of the [`{enum_ident}`] variant.
72
73# Notes
74
75This method is generated by the [`Variants`] derive macro from the [`beerec-variants`]
76crate, it applies rename strategies on the string representation of the
77variant, following a priority-based fallback approach:
78
791. [`InnerRenameStrategy`] (_highest priority_) - uses the abbreviated
80   string produced by the rename strategy from the `#[variants(rename_abbr(...))]`
81   attribute, if one has been specified for the variant;
821. [`OuterRenameStrategy`] (_fallback_) - uses the abbreviated string produced
83   by the rename strategy from the `#[variants(rename_abbr(...))]` attribute, if
84   one has been specified for the type;
851. **No renaming** (_default_) - abbreviates the full length string representation
86   of the variant as is, without applying any renaming strategy.
87
88Likewise, the renaming follows a priority-based fallback approach to
89determine the full length string representation before applying the
90abbreviation:
91
921. **Variant-level attribute** (_highest priority_) - uses the string
93   produced by the rename strategy from the `#[variants(rename(...))]`
94   attribute, if one has been specified for the type;
951. **Type-level attribute** (_fallback_) - uses the string produced by the
96   rename strategy from the `#[variants(rename(...))]` attribute, if one has
97   been specified for the type;
981. **No renaming** (_default_) - converts the variant identifier to a string
99   if neither the type-level nor the variant-level rename attribute has been
100   specified.
101
102[`beerec-variants`]: https://docs.rs/beerec-variants
103[`Variants`]: https://docs.rs/beerec-variants/latest/beerec_variants/derive.Variants.html"
104    );
105
106    let iter_variants_doc = format!(
107        r"Iterates over _iterable_ (i.e. non-skipped) [`{enum_ident}`] variants.
108
109# Notes
110
111This method is generated by the [`Variants`] derive macro from the [`beerec-variants`] crate,
112enum variants marked with the `#[variants(skip)]` attribute are excluded from iteration.
113
114[`beerec-variants`]: https://docs.rs/beerec-variants
115[`Variants`]: https://docs.rs/beerec-variants/latest/beerec_variants/derive.Variants.html"
116    );
117
118    let iter_variants_as_str_doc = format!(
119        r"Iterates over _iterable_ (i.e. non-skipped) string representations of [`{enum_ident}`]
120variants.
121
122See [`{enum_ident}::as_str`] for further details about yielded values.
123
124# Notes
125
126This method is generated by the [`Variants`] derive macro from the [`beerec-variants`] crate,
127enum variants marked with the `#[variants(skip)]` attribute are excluded from iteration.
128
129[`beerec-variants`]: https://docs.rs/beerec-variants
130[`Variants`]: https://docs.rs/beerec-variants/latest/beerec_variants/derive.Variants.html"
131    );
132
133    let iter_variants_as_str_abbr_doc = format!(
134        r"Iterates over _iterable_ (i.e. non-skipped) abbreviated string representations of
135[`{enum_ident}`] variants.
136
137See [`{enum_ident}::as_str_abbr`] for further details about yielded values.
138
139# Notes
140
141This method is generated by the [`Variants`] derive macro from the [`beerec-variants`] crate,
142enum variants marked with the `#[variants(skip)]` attribute are excluded from iteration.
143
144[`beerec-variants`]: https://docs.rs/beerec-variants
145[`Variants`]: https://docs.rs/beerec-variants/latest/beerec_variants/derive.Variants.html"
146    );
147
148    let variants_list_str_doc = format!(
149        r"Returns a list of quoted (double-quotes) and comma separated string
150representations of _iterable_ (i.e. non-skipped) [`{enum_ident}`] variants.
151
152See [`{enum_ident}::as_str`] for further details about the string representations.
153
154# Notes
155
156This method is generated by the [`Variants`] derive macro from the [`beerec-variants`] crate,
157enum variants marked with the `#[variants(skip)]` attribute are excluded from the listing.
158
159[`beerec-variants`]: https://docs.rs/beerec-variants
160[`Variants`]: https://docs.rs/beerec-variants/latest/beerec_variants/derive.Variants.html"
161    );
162
163    let variants_list_str_abbr_doc = format!(
164        r"Returns a list of quoted (double-quotes) and comma separated abbreviated string
165representations of _iterable_ (i.e. non-skipped) [`{enum_ident}`] variants.
166
167See [`{enum_ident}::as_str_abbr`] for further details about the abbreviated string representations.
168
169# Notes
170
171This method is generated by the [`Variants`] derive macro from the [`beerec-variants`] crate,
172enum variants marked with the `#[variants(skip)]` attribute are excluded from the listing.
173
174[`beerec-variants`]: https://docs.rs/beerec-variants
175[`Variants`]: https://docs.rs/beerec-variants/latest/beerec_variants/derive.Variants.html"
176    );
177
178    let mut generated = quote::quote! {
179        impl ::std::marker::Copy for #enum_ident {}
180
181        impl ::std::clone::Clone for #enum_ident {
182            fn clone(&self) -> Self {
183                *self
184            }
185        }
186
187        #[automatically_derived]
188        impl #enum_ident {
189            #[doc = #iterable_variants_doc]
190            const ITERABLE_VARIANTS: [Self; #variants_count] = [
191                #(Self::#variants_idents,)*
192            ];
193
194            #[doc = #iterable_variants_count_doc]
195            const ITERABLE_VARIANTS_COUNT: usize = #variants_count;
196
197            #[inline]
198            #[must_use]
199            #[doc = #as_str_doc]
200            pub const fn as_str(self) -> &'static str {
201                match self {
202                    #(#variants_as_str_match_branches,)*
203                }
204            }
205
206            #[inline]
207            #[must_use]
208            #[doc = #as_str_abbr_doc]
209            pub const fn as_str_abbr(self) -> &'static str {
210                match self {
211                    #(#variants_as_str_abbr_match_branches,)*
212                }
213            }
214
215            #[doc = #iter_variants_doc]
216            pub fn iter_variants() -> impl ::std::iter::Iterator<Item = Self> {
217                Self::ITERABLE_VARIANTS.into_iter()
218            }
219
220            #[doc = #iter_variants_as_str_doc]
221            pub fn iter_variants_as_str() -> impl ::std::iter::Iterator<Item = &'static str> {
222                Self::iter_variants().map(Self::as_str)
223            }
224
225            #[doc = #iter_variants_as_str_abbr_doc]
226            pub fn iter_variants_as_str_abbr() -> impl ::std::iter::Iterator<Item = &'static str> {
227                Self::iter_variants().map(Self::as_str_abbr)
228            }
229
230            #[doc = #variants_list_str_doc]
231            pub const fn variants_list_str() -> &'static str {
232                #variants_list_string
233            }
234
235            #[doc = #variants_list_str_abbr_doc]
236            pub const fn variants_list_str_abbr() -> &'static str {
237                #variants_list_string_abbr
238            }
239        }
240    };
241
242    if target_enum.implement_display() {
243        let generated_display_impl = quote::quote! {
244            impl ::std::fmt::Display for #enum_ident {
245                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
246                    ::std::fmt::Formatter::write_str(f, self.as_str())
247                }
248            }
249        };
250
251        generated.extend(generated_display_impl);
252    }
253
254    if target_enum.implement_from_str() {
255        let parse_error_ident = Ident::new(&format!("Parse{enum_ident}Error"), Span::call_site());
256        let variants_from_str_match_branches = target_enum.variants_from_str_match_branches();
257
258        let generated_from_str_impl = quote::quote! {
259            #[derive(Debug, PartialEq, Eq)]
260            pub struct #parse_error_ident;
261
262            impl ::std::fmt::Display for #parse_error_ident {
263                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
264                    ::std::fmt::Formatter::write_str(f, "Expected one of ")?;
265                    ::std::fmt::Formatter::write_str(f, #enum_ident::variants_list_str())?;
266                    ::std::fmt::Formatter::write_str(f, " or one of ")?;
267                    ::std::fmt::Formatter::write_str(f, #enum_ident::variants_list_str_abbr())?;
268
269                    Ok(())
270                }
271            }
272
273            impl ::std::error::Error for #parse_error_ident {}
274
275            impl ::std::str::FromStr for #enum_ident {
276                type Err = #parse_error_ident;
277
278                fn from_str(value: &str) -> ::std::result::Result<Self, Self::Err> {
279                    match value {
280                        #(#variants_from_str_match_branches,)*
281                        _ => ::std::result::Result::Err(#parse_error_ident),
282                    }
283                }
284            }
285        };
286
287        generated.extend(generated_from_str_impl);
288    }
289
290    #[cfg(feature = "serde")]
291    if target_enum.implement_deserialize() {
292        let visitor_ident = Ident::new(&format!("{enum_ident}Visitor"), Span::call_site());
293        let variants_deserialize_match_branches = target_enum.variants_deserialize_match_branches();
294
295        let generated_deserialize_impl = quote::quote! {
296            impl<'de> ::serde::de::Deserialize<'de> for #enum_ident {
297                fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
298                where
299                    D: ::serde::de::Deserializer<'de>
300                {
301                    struct #visitor_ident;
302
303                    impl<'de> ::serde::de::Visitor<'de> for #visitor_ident {
304                        type Value = #enum_ident;
305
306                        fn expecting(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
307                            ::std::fmt::Formatter::write_str(f, "one of ")?;
308                            ::std::fmt::Formatter::write_str(f, #enum_ident::variants_list_str())?;
309                            ::std::fmt::Formatter::write_str(f, " or one of ")?;
310                            ::std::fmt::Formatter::write_str(f, #enum_ident::variants_list_str_abbr())?;
311
312                            Ok(())
313                        }
314
315                        fn visit_str<E>(self, value: &str) -> ::std::result::Result<Self::Value, E>
316                        where
317                            E: ::serde::de::Error,
318                        {
319                            match value {
320                                #(#variants_deserialize_match_branches,)*
321                                _ => {
322                                    let unexp = ::serde::de::Unexpected::Str(value);
323                                    let error = ::serde::de::Error::invalid_value(unexp, &self);
324                                    ::std::result::Result::Err(error)
325                                },
326                            }
327                        }
328                    }
329
330                    deserializer.deserialize_str(#visitor_ident)
331                }
332            }
333        };
334
335        generated.extend(generated_deserialize_impl);
336    }
337
338    #[cfg(feature = "serde")]
339    if target_enum.implement_serialize() {
340        let generated_serialize_impl = quote::quote! {
341            impl ::serde::ser::Serialize for #enum_ident {
342                fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
343                where
344                    S: ::serde::ser::Serializer,
345                {
346                    serializer.serialize_str(self.as_str())
347                }
348            }
349        };
350
351        generated.extend(generated_serialize_impl);
352    }
353
354    Ok(generated)
355}
356
357/// Derive macro to generate boilerplate on unit variants `enum` types.
358///
359/// The macro generates the following methods:
360///
361/// - `as_str` - returns a string representation of the target `enum` variant;
362/// - `as_str_abbr` - returns an abbreviated string representation of the target
363///   `enum` variant;
364/// - `iter_variants` - returns an iterator over target `enum` variants (owned
365///   values);
366/// - `iter_variants_as_str` - returns an iterator over string representations
367///   of the target `enum` variants (`&'static str` values);
368/// - `iter_variants_as_str_abbr` - returns an iterator over abbreviated string
369///   representations of the `enum` variants (`&'static str` values);
370/// - `variants_list_str` - returns a list of quoted (double-quotes) and comma
371///   separated string representations of the `enum` variants;
372/// - `variants_list_str_abbr` - returns a list of of quoted (double-quotes) and
373///   comma separated abbreviated string representation of the `enum` variants.
374///
375/// # Enum level attributes
376///
377/// The macro exposes the following `enum` outer attributes (i.e. attributes to
378/// be applied to the `enum` type the macro is being derived on):
379///
380/// - `rename` - customizes the string representation of each variant;
381/// - `rename_abbr` - customizes the abbreviated string representation of each
382///   variant;
383/// - `display` - generates a [`Display`] trait implementation based on the
384///   string representation provided by the generated `as_str` method;
385/// - `from_str` - generates a [`FromStr`] trait implementation based on the
386///   string or abbreviated string representation provided by the generated
387///   `as_str` and `as_str_abbr` methods respectively.
388///
389/// Valid `rename` and `rename_abbr` customization strategies are:
390///
391/// - `uppercase` - makes the (abbreviated) string representation uppercase;
392/// - `lowercase` - makes the (abbreviated) string representation lowercase.
393///
394/// ## Examples
395///
396/// ```rust
397/// # use beerec_variants::Variants;
398/// #
399/// #[derive(Variants)]
400/// #[variants(rename(uppercase))]
401/// enum CardinalDirection {
402///     North,
403///     East,
404///     South,
405///     West,
406/// }
407///
408/// # fn main() {
409/// assert_eq!("NORTH", CardinalDirection::North.as_str());
410/// assert_eq!("EAST", CardinalDirection::East.as_str());
411/// assert_eq!("SOUTH", CardinalDirection::South.as_str());
412/// assert_eq!("WEST", CardinalDirection::West.as_str());
413///
414/// assert_eq!("NOR", CardinalDirection::North.as_str_abbr());
415/// assert_eq!("EAS", CardinalDirection::East.as_str_abbr());
416/// assert_eq!("SOU", CardinalDirection::South.as_str_abbr());
417/// assert_eq!("WES", CardinalDirection::West.as_str_abbr());
418/// # }
419/// ```
420///
421/// ```rust
422/// # use beerec_variants::Variants;
423/// #
424/// #[derive(Variants)]
425/// #[variants(rename(lowercase), rename_abbr(uppercase))]
426/// enum State {
427///     Active,
428///     Inactive,
429///     Disabled,
430/// }
431///
432/// # fn main() {
433/// assert_eq!("active", State::Active.as_str());
434/// assert_eq!("inactive", State::Inactive.as_str());
435/// assert_eq!("disabled", State::Disabled.as_str());
436///
437/// assert_eq!("ACT", State::Active.as_str_abbr());
438/// assert_eq!("INA", State::Inactive.as_str_abbr());
439/// assert_eq!("DIS", State::Disabled.as_str_abbr());
440/// # }
441/// ```
442///
443/// ```rust
444/// # use beerec_variants::Variants;
445/// #
446/// #[derive(Variants)]
447/// #[variants(display)]
448/// enum Season {
449///     Spring,
450///     Summer,
451///     Autumn,
452///     Winter,
453/// }
454///
455/// # fn main() {
456/// assert_eq!(String::from("Spring"), Season::Spring.to_string());
457/// assert_eq!(String::from("Summer"), Season::Summer.to_string());
458/// assert_eq!(String::from("Autumn"), Season::Autumn.to_string());
459/// assert_eq!(String::from("Winter"), Season::Winter.to_string());
460///
461/// assert_eq!(String::from("Spring"), format!("{}", Season::Spring));
462/// assert_eq!(String::from("Summer"), format!("{}", Season::Summer));
463/// assert_eq!(String::from("Autumn"), format!("{}", Season::Autumn));
464/// assert_eq!(String::from("Winter"), format!("{}", Season::Winter));
465/// # }
466/// ```
467///
468/// ```rust
469/// # use std::str::FromStr;
470/// #
471/// # use beerec_variants::Variants;
472/// #
473/// #[derive(Debug, Variants, PartialEq, Eq)]
474/// #[variants(from_str)]
475/// enum Priority {
476///     Low,
477///     Medium,
478///     High,
479///     Critical,
480/// }
481///
482/// # fn main() {
483/// assert_eq!(Ok(Priority::Low), <Priority as FromStr>::from_str("Low"));
484/// assert_eq!(Ok(Priority::Medium), <Priority as FromStr>::from_str("Medium"));
485/// assert_eq!(Ok(Priority::High), <Priority as FromStr>::from_str("High"));
486/// assert_eq!(Ok(Priority::Critical), <Priority as FromStr>::from_str("Critical"));
487///
488/// assert_eq!(Ok(Priority::Low), <Priority as FromStr>::from_str("Low"));
489/// assert_eq!(Ok(Priority::Medium), <Priority as FromStr>::from_str("Med"));
490/// assert_eq!(Ok(Priority::High), <Priority as FromStr>::from_str("Hig"));
491/// assert_eq!(Ok(Priority::Critical), <Priority as FromStr>::from_str("Cri"));
492///
493/// assert_eq!(Err(ParsePriorityError), <Priority as FromStr>::from_str("invalid"));
494/// # }
495/// ```
496///
497/// ## Feature-gated attributes
498///
499/// ### [Serde](https://crates.io/crates/serde)
500///
501/// The following `enum` outer attributes are exposed when the `serde` feature is
502/// enabled:
503/// 
504/// - `deserialize` - generates a [`Deserialize`] trait implementation based on
505///   the string or abbreviated string representation provided by the generated
506///   `as_str` and `as_str_abbr` respectively;
507/// - `serialize` - generates a [`Serialize`] trait implementation based on the
508///   string representation provided by the generated `as_str` method.
509/// 
510/// #### Examples
511///
512/// ```rust
513/// # use beerec_variants::Variants;
514/// #
515/// #[derive(Debug, Variants, PartialEq, Eq)]
516/// # #[cfg(feature = "serde")]
517/// #[variants(deserialize)]
518/// enum Theme {
519///     Auto,
520///     Dark,
521///     Light,
522/// }
523///
524/// #[derive(Debug, PartialEq, Eq)]
525/// # #[cfg(feature = "serde")]
526/// #[derive(serde::Deserialize)]
527/// struct Config {
528///     theme: Theme,
529/// }
530///
531/// # fn main() {
532/// # #[cfg(feature = "serde")]
533/// # {
534/// // Deserialize from variant string representation.
535/// assert_eq!(
536///     Ok(Config { theme: Theme::Auto }),
537///     toml::from_str::<'_, Config>("theme = \"Auto\"\n"),
538/// );
539///
540/// assert_eq!(
541///     Ok(Config { theme: Theme::Dark }),
542///     toml::from_str::<'_, Config>("theme = \"Dark\"\n"),
543/// );
544///
545/// assert_eq!(
546///     Ok(Config { theme: Theme::Light }),
547///     toml::from_str::<'_, Config>("theme = \"Light\"\n"),
548/// );
549///
550/// // Deserialize from variant abbreviated string representation.
551/// assert_eq!(
552///     Ok(Config { theme: Theme::Auto }),
553///     toml::from_str::<'_, Config>("theme = \"Aut\"\n"),
554/// );
555///
556/// assert_eq!(
557///     Ok(Config { theme: Theme::Dark }),
558///     toml::from_str::<'_, Config>("theme = \"Dar\"\n"),
559/// );
560///
561/// assert_eq!(
562///     Ok(Config { theme: Theme::Light }),
563///     toml::from_str::<'_, Config>("theme = \"Lig\"\n"),
564/// );
565/// # }
566/// # }
567/// ```
568///
569/// ```rust
570/// # use beerec_variants::Variants;
571/// #
572/// #[derive(Debug, Variants, PartialEq, Eq)]
573/// # #[cfg(feature = "serde")]
574/// #[variants(serialize)]
575/// enum Codec {
576///     H264,
577///     H265,
578///     AV1,
579/// }
580///
581/// #[derive(Debug, PartialEq, Eq)]
582/// # #[cfg(feature = "serde")]
583/// #[derive(serde::Serialize)]
584/// struct Config {
585///     codec: Codec,
586/// }
587///
588/// # fn main() {
589/// # #[cfg(feature = "serde")]
590/// # {
591/// assert_eq!(
592///     Ok(String::from("codec = \"H264\"\n")),
593///     toml::to_string(&Config { codec: Codec::H264 }),
594/// );
595///
596/// assert_eq!(
597///     Ok(String::from("codec = \"H265\"\n")),
598///     toml::to_string(&Config { codec: Codec::H265 }),
599/// );
600///
601/// assert_eq!(
602///     Ok(String::from("codec = \"AV1\"\n")),
603///     toml::to_string(&Config { codec: Codec::AV1 }),
604/// );
605/// # }
606/// # }
607/// ```
608///
609/// # Variant level attributes
610///
611/// The macro exposes the following variant attributes:
612///
613/// - `skip` - excludes the marked variant from iteration and listing;
614/// - `rename` - customizes the string representation of the marked variant;
615/// - `rename_abbr` - customizes the abbreviated string representation of the
616///   marked variant.
617///
618/// Valid `rename` and `rename_abbr` customization strategies are:
619///
620/// - `"..."` (string literal) - overrides the string representation with a
621///   custom string;
622/// - `uppercase` - makes the (abbreviated) string representation uppercase;
623/// - `lowercase` - makes the (abbreviated) string representation lowercase.
624///
625/// For custom string overrides:
626///
627/// - `#[variants(rename = "...")]` is equivalent to
628///   `#[variants(rename("..."))]`;
629/// - `#[variants(rename_abbr = "...")]` is equivalent to
630///   `#[variants(rename_abbr("..."))]`;
631///
632/// both are valid, supported formats.
633///
634/// ## Examples
635///
636/// ```rust
637/// # use beerec_variants::Variants;
638/// #
639/// #[derive(Variants)]
640/// enum Format {
641///     Xml,
642///     Csv,
643///     #[variants(rename("plain-text"), rename_abbr = "txt")]
644///     PlainText,
645/// }
646///
647/// # fn main() {
648/// assert_eq!("Xml", Format::Xml.as_str());
649/// assert_eq!("Csv", Format::Csv.as_str());
650/// assert_eq!("plain-text", Format::PlainText.as_str());
651///
652/// assert_eq!("Xml", Format::Xml.as_str_abbr());
653/// assert_eq!("Csv", Format::Csv.as_str_abbr());
654/// assert_eq!("txt", Format::PlainText.as_str_abbr());
655/// # }
656/// ```
657///
658/// # String representation renaming priority
659///
660/// To produce _string representations_ of enum variants, renaming can be
661/// applied at both the type level and variant level. The string representation
662/// of each variant is obtained by applying rename strategies following a
663/// priority-based fallback approach:
664///
665/// 1. **Variant-level attribute** (_highest priority_) - usese the string
666///    produced by the rename strategy from the `#[variants(rename(...))]`
667///    attribute, if one has been specified for the variant;
668/// 1. **Type-level attribute** (_fallback_) - uses the string produced by the
669///    rename strategy from the `#[variants(rename(...))]` attribute, if one has
670///    been specified for the type;
671/// 1. **No renaming** (_default_) - converts the variant identifier to a string
672///    if neither the type-level nor the variant-level rename attribute has been
673///    specified.
674///
675/// # Abbreviated string representation renaming priority
676///
677/// To produce _abbreviated string representations_ of the enum variants,
678/// renaming can be applied at both the type level and the variant level. The
679/// abbreviated string representation of each variant is obtained by applying
680/// rename strategies following a priority-based fallback approach:
681///
682/// 1. **Variant-level attribute** (_highest priority_) - uses the abbreviated
683///    string produced by the rename strategy from the
684///    `#[variants(rename_abbr(...))]` attribute, if one has been specified for
685///    the variant;
686/// 1. **Type-level attribute** (_fallback_) - uses the string produced by the
687///    rename strategy from the `#[variants(rename(...))]` attribute, if one has
688///    been specified for the type;
689/// 1. **No renaming** (_default_) - abbreviates the full length string
690///    representation of the variant as is, without applying any renaming
691///    strategy.
692///
693/// Likewise, the renaming follows a priority-based fallback approach to
694/// determine the full length string representation before applying the
695/// abbreviation:
696///
697/// 1. **Variant-level attribute** (_highest priority_) - uses the string
698///    produced by the rename strategy from the `#[variants(rename(...))]`
699///    attribute, if one has been specified for the type;
700/// 1. **Type-level attribute** (_fallback_) - uses the string produced by the
701///    rename strategy from the `#[variants(rename(...))]` attribute, if one has
702///    been specified for the type;
703/// 1. **No renaming** (_default_) - converts the variant identifier to a string
704///    if neither the type-level nor the variant-level rename attribute has been
705///    specified.
706///
707/// # Errors
708///
709/// The macro will produce a compile error if:
710///
711/// - derived on `struct` types;
712/// - derived on `union` types;
713/// - derived on `enum` types with any named field variants;
714/// - derived on `enum` types with any unnamed field (i.e. tuple) variants;
715/// - derived on `enum` types with any newtype variants;
716/// - the `rename` variant-level attribute is passed any other value than a
717///   string literal, `uppercase` or `lowercase`;
718/// - the `rename_abbr` variant-level attribute is passed any other value than a
719///   string literal, `uppercase` or `lowercase`;
720/// - the `rename` type-level attribute is passed any other value than
721///   `uppercase` or `lowercase`;
722/// - the `rename_abbr` type-level attribute is passed any other value than
723///   `uppercase` or `lowercase`.
724///
725/// # Notes
726///
727/// Deriving [`Variants`] on type automatically implements [`Clone`] and
728/// [`Copy`] for such type. This means that deriving either trait on a type that
729/// also derives [`Variants`] will result in a "conflicting implementations"
730/// compilation error.
731///
732/// # Examples
733///
734/// ```rust
735/// # use std::str::FromStr;
736/// #
737/// # use beerec_variants::Variants;
738/// #
739/// #[derive(Variants, Debug, PartialEq, Eq)]
740/// #[cfg_attr(feature = "serde", variants(deserialize, serialize))]
741/// #[variants(display, from_str)]
742/// enum Weekday {
743///     #[variants(skip)]
744///     Monday,
745///     #[variants(rename = "DayAfterMonday", rename_abbr = "tue")]
746///     Tuesday,
747///     #[variants(rename_abbr = "wed")]
748///     Wednesday,
749///     #[variants(rename = "Giovedì", rename_abbr(lowercase))]
750///     Thursday,
751///     Friday,
752///     Saturday,
753///     Sunday,
754/// }
755///
756/// #[derive(Debug, PartialEq, Eq)]
757/// #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
758/// struct Schedule {
759///     weekday: Weekday,
760/// }
761///
762/// # fn main() {
763/// // Monday has been marked as `skip`, iterator will yield 6 values.
764/// assert_eq!(6, Weekday::iter_variants().count());
765///
766/// assert_eq!("Monday", Weekday::Monday.as_str());
767/// assert_eq!("DayAfterMonday", Weekday::Tuesday.as_str());
768/// assert_eq!("Wednesday", Weekday::Wednesday.as_str());
769/// assert_eq!("Giovedì", Weekday::Thursday.as_str());
770/// assert_eq!("Friday", Weekday::Friday.as_str());
771/// assert_eq!("Saturday", Weekday::Saturday.as_str());
772/// assert_eq!("Sunday", Weekday::Sunday.as_str());
773///
774/// assert_eq!("Mon", Weekday::Monday.as_str_abbr());
775/// assert_eq!("tue", Weekday::Tuesday.as_str_abbr());
776/// assert_eq!("wed", Weekday::Wednesday.as_str_abbr());
777/// assert_eq!("gio", Weekday::Thursday.as_str_abbr());
778/// assert_eq!("Fri", Weekday::Friday.as_str_abbr());
779/// assert_eq!("Sat", Weekday::Saturday.as_str_abbr());
780/// assert_eq!("Sun", Weekday::Sunday.as_str_abbr());
781///
782/// // The enum has been marked as `display`, so `std::fmt::Display` implementation is available.
783/// assert_eq!(String::from("Monday"), Weekday::Monday.to_string());
784/// assert_eq!(String::from("DayAfterMonday"), Weekday::Tuesday.to_string());
785/// assert_eq!(String::from("Wednesday"), Weekday::Wednesday.to_string());
786/// assert_eq!(String::from("Giovedì"), Weekday::Thursday.to_string());
787/// assert_eq!(String::from("Friday"), Weekday::Friday.to_string());
788/// assert_eq!(String::from("Saturday"), Weekday::Saturday.to_string());
789/// assert_eq!(String::from("Sunday"), Weekday::Sunday.to_string());
790///
791/// assert_eq!(String::from("Monday"), format!("{}", Weekday::Monday));
792/// assert_eq!(String::from("DayAfterMonday"), format!("{}", Weekday::Tuesday));
793/// assert_eq!(String::from("Wednesday"), format!("{}", Weekday::Wednesday));
794/// assert_eq!(String::from("Giovedì"), format!("{}", Weekday::Thursday));
795/// assert_eq!(String::from("Friday"), format!("{}", Weekday::Friday));
796/// assert_eq!(String::from("Saturday"), format!("{}", Weekday::Saturday));
797/// assert_eq!(String::from("Sunday"), format!("{}", Weekday::Sunday));
798///
799/// let mut weekdays = Weekday::iter_variants();
800/// assert_eq!(Some(Weekday::Tuesday), weekdays.next());
801/// assert_eq!(Some(Weekday::Wednesday), weekdays.next());
802/// assert_eq!(Some(Weekday::Thursday), weekdays.next());
803/// assert_eq!(Some(Weekday::Friday), weekdays.next());
804/// assert_eq!(Some(Weekday::Saturday), weekdays.next());
805/// assert_eq!(Some(Weekday::Sunday), weekdays.next());
806/// assert_eq!(None, weekdays.next());
807///
808/// let mut weekdays_as_str = Weekday::iter_variants_as_str();
809/// assert_eq!(Some("DayAfterMonday"), weekdays_as_str.next());
810/// assert_eq!(Some("Wednesday"), weekdays_as_str.next());
811/// assert_eq!(Some("Giovedì"), weekdays_as_str.next());
812/// assert_eq!(Some("Friday"), weekdays_as_str.next());
813/// assert_eq!(Some("Saturday"), weekdays_as_str.next());
814/// assert_eq!(Some("Sunday"), weekdays_as_str.next());
815/// assert_eq!(None, weekdays.next());
816///
817/// let mut weekdays_as_str_abbr = Weekday::iter_variants_as_str_abbr();
818/// assert_eq!(Some("tue"), weekdays_as_str_abbr.next());
819/// assert_eq!(Some("wed"), weekdays_as_str_abbr.next());
820/// assert_eq!(Some("gio"), weekdays_as_str_abbr.next());
821/// assert_eq!(Some("Fri"), weekdays_as_str_abbr.next());
822/// assert_eq!(Some("Sat"), weekdays_as_str_abbr.next());
823/// assert_eq!(Some("Sun"), weekdays_as_str_abbr.next());
824/// assert_eq!(None, weekdays.next());
825///
826/// assert_eq!(
827///     "\"DayAfterMonday\", \"Wednesday\", \"Giovedì\", \"Friday\", \"Saturday\", \"Sunday\"",
828///     Weekday::variants_list_str(),
829/// );
830///
831/// assert_eq!(
832///     "\"tue\", \"wed\", \"gio\", \"Fri\", \"Sat\", \"Sun\"",
833///     Weekday::variants_list_str_abbr(),
834/// );
835///
836/// // The enum has been marked as `from_str`, so `std::str::FromStr` implementation is available.
837/// assert_eq!(Ok(Weekday::Monday), <Weekday as FromStr>::from_str("Monday"));
838/// assert_eq!(Ok(Weekday::Tuesday), <Weekday as FromStr>::from_str("DayAfterMonday"));
839/// assert_eq!(Ok(Weekday::Wednesday), <Weekday as FromStr>::from_str("Wednesday"));
840/// assert_eq!(Ok(Weekday::Thursday), <Weekday as FromStr>::from_str("Giovedì"));
841/// assert_eq!(Ok(Weekday::Friday), <Weekday as FromStr>::from_str("Friday"));
842/// assert_eq!(Ok(Weekday::Saturday), <Weekday as FromStr>::from_str("Saturday"));
843/// assert_eq!(Ok(Weekday::Sunday), <Weekday as FromStr>::from_str("Sunday"));
844///
845/// assert_eq!(Ok(Weekday::Monday), <Weekday as FromStr>::from_str("Mon"));
846/// assert_eq!(Ok(Weekday::Tuesday), <Weekday as FromStr>::from_str("tue"));
847/// assert_eq!(Ok(Weekday::Wednesday), <Weekday as FromStr>::from_str("wed"));
848/// assert_eq!(Ok(Weekday::Thursday), <Weekday as FromStr>::from_str("gio"));
849/// assert_eq!(Ok(Weekday::Friday), <Weekday as FromStr>::from_str("Fri"));
850/// assert_eq!(Ok(Weekday::Saturday), <Weekday as FromStr>::from_str("Sat"));
851/// assert_eq!(Ok(Weekday::Sunday), <Weekday as FromStr>::from_str("Sun"));
852///
853/// assert_eq!(Err(ParseWeekdayError), <Weekday as FromStr>::from_str("invalid"));
854///
855/// // The enum has been marked as `deserialize`, so `serde::Deserialize` implementation is available.
856/// #[cfg(feature = "serde")]
857/// {
858///     // Deserialize from variant string representation.
859///     assert_eq!(
860///         Ok(Schedule { weekday: Weekday::Monday }),
861///         toml::from_str::<'_, Schedule>("weekday = \"Monday\"\n"),
862///     );
863///
864///     assert_eq!(
865///         Ok(Schedule { weekday: Weekday::Tuesday }),
866///         toml::from_str::<'_, Schedule>("weekday = \"DayAfterMonday\"\n"),
867///     );
868///
869///     assert_eq!(
870///         Ok(Schedule { weekday: Weekday::Wednesday }),
871///         toml::from_str::<'_, Schedule>("weekday = \"Wednesday\"\n"),
872///     );
873///
874///     assert_eq!(
875///         Ok(Schedule { weekday: Weekday::Thursday }),
876///         toml::from_str::<'_, Schedule>("weekday = \"Giovedì\"\n"),
877///     );
878///
879///     assert_eq!(
880///         Ok(Schedule { weekday: Weekday::Friday }),
881///         toml::from_str::<'_, Schedule>("weekday = \"Friday\"\n"),
882///     );
883///
884///     assert_eq!(
885///         Ok(Schedule { weekday: Weekday::Saturday }),
886///         toml::from_str::<'_, Schedule>("weekday = \"Saturday\"\n"),
887///     );
888///
889///     assert_eq!(
890///         Ok(Schedule { weekday: Weekday::Sunday }),
891///         toml::from_str::<'_, Schedule>("weekday = \"Sunday\"\n"),
892///     );
893///
894///     // Deserialize from variant abbreviated string representation.
895///     assert_eq!(
896///         Ok(Schedule { weekday: Weekday::Monday }),
897///         toml::from_str::<'_, Schedule>("weekday = \"Mon\"\n"),
898///     );
899///
900///     assert_eq!(
901///         Ok(Schedule { weekday: Weekday::Tuesday }),
902///         toml::from_str::<'_, Schedule>("weekday = \"tue\"\n"),
903///     );
904///
905///     assert_eq!(
906///         Ok(Schedule { weekday: Weekday::Wednesday }),
907///         toml::from_str::<'_, Schedule>("weekday = \"wed\"\n"),
908///     );
909///
910///     assert_eq!(
911///         Ok(Schedule { weekday: Weekday::Thursday }),
912///         toml::from_str::<'_, Schedule>("weekday = \"gio\"\n"),
913///     );
914///
915///     assert_eq!(
916///         Ok(Schedule { weekday: Weekday::Friday }),
917///         toml::from_str::<'_, Schedule>("weekday = \"Fri\"\n"),
918///     );
919///
920///     assert_eq!(
921///         Ok(Schedule { weekday: Weekday::Saturday }),
922///         toml::from_str::<'_, Schedule>("weekday = \"Sat\"\n"),
923///     );
924///
925///     assert_eq!(
926///         Ok(Schedule { weekday: Weekday::Sunday }),
927///         toml::from_str::<'_, Schedule>("weekday = \"Sun\"\n"),
928///     );
929/// }
930/// 
931/// // The enum has been marked as `serialize`, so `serde::Serialize` implementation is available.
932/// #[cfg(feature = "serde")]
933/// {
934///     assert_eq!(
935///         Ok(String::from("weekday = \"Monday\"\n")),
936///         toml::to_string(&Schedule { weekday: Weekday::Monday }),
937///     );
938///
939///     assert_eq!(
940///         Ok(String::from("weekday = \"DayAfterMonday\"\n")),
941///         toml::to_string(&Schedule { weekday: Weekday::Tuesday }),
942///     );
943///
944///     assert_eq!(
945///         Ok(String::from("weekday = \"Wednesday\"\n")),
946///         toml::to_string(&Schedule { weekday: Weekday::Wednesday }),
947///     );
948///
949///     assert_eq!(
950///         Ok(String::from("weekday = \"Giovedì\"\n")),
951///         toml::to_string(&Schedule { weekday: Weekday::Thursday }),
952///     );
953///
954///     assert_eq!(
955///         Ok(String::from("weekday = \"Friday\"\n")),
956///         toml::to_string(&Schedule { weekday: Weekday::Friday }),
957///     );
958///
959///     assert_eq!(
960///         Ok(String::from("weekday = \"Saturday\"\n")),
961///         toml::to_string(&Schedule { weekday: Weekday::Saturday }),
962///     );
963///
964///     assert_eq!(
965///         Ok(String::from("weekday = \"Sunday\"\n")),
966///         toml::to_string(&Schedule { weekday: Weekday::Sunday }),
967///     );
968/// }
969/// # }
970/// 
971/// ```
972///
973/// [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html
974/// [`Copy`]: https://doc.rust-lang.org/std/marker/trait.Copy.html
975/// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
976/// [`FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html
977/// [`Deserialize`]: https://docs.rs/serde/latest/serde/trait.Deserialize.html
978/// [`Serialize`]: https://docs.rs/serde/latest/serde/trait.Serialize.html
979#[proc_macro_derive(Variants, attributes(variants))]
980pub fn derive_enum_variants(input: TokenStream) -> TokenStream {
981    let input = syn::parse_macro_input!(input as DeriveInput);
982
983    derive_enum_variants_impl(&input)
984        .unwrap_or_else(syn::Error::into_compile_error)
985        .into()
986}
987
988#[cfg(test)]
989mod test {
990    #[test]
991    fn expand() {
992        macrotest::expand("tests/expand/*.rs");
993    }
994
995    #[test]
996    fn expand_serde() {
997        macrotest::expand_args("tests/expand/serde/*.rs", &["--features", "serde"]);
998    }
999
1000    #[test]
1001    fn error() {
1002        let test = trybuild::TestCases::new();
1003        test.compile_fail("tests/fail/*.rs");
1004    }
1005}