enum_dispatch/
enum_dispatch_item.rs

1//! Provides an implementation of a `syn`- and `quote`-compatible syntax item describing the
2//! shortened enum form used by `enum_dispatch`.
3//!
4//! The syntax is *mostly* identical to that of standard enums. The only difference is the
5//! specification of enum variants -- in the custom `EnumDispatchItem` type, each variant must be
6//! specified as a `syn::Type` rather than a `syn::Variant`. In the case of basic unit fields named
7//! after existing scoped types, a normal Rust enum can be parsed as an EnumDispatchItem without
8//! issue.
9use quote::TokenStreamExt;
10
11use crate::enum_dispatch_variant::EnumDispatchVariant;
12use crate::filter_attrs::FilterAttrs;
13
14/// A structure that can be used to store syntax information about an `enum_dispatch` enum.
15///
16/// Mostly identical to `syn::ItemEnum`.
17#[derive(Clone)]
18pub struct EnumDispatchItem {
19    pub attrs: Vec<syn::Attribute>,
20    pub vis: syn::Visibility,
21    enum_token: syn::token::Enum,
22    pub ident: syn::Ident,
23    pub generics: syn::Generics,
24    brace_token: syn::token::Brace,
25    pub variants: syn::punctuated::Punctuated<EnumDispatchVariant, syn::token::Comma>,
26}
27
28/// Allows `EnumDispatchItem`s to be parsed from `String`s or `TokenStream`s.
29impl syn::parse::Parse for EnumDispatchItem {
30    fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
31        let attrs = input.call(syn::Attribute::parse_outer)?;
32        let vis: syn::Visibility = input.parse()?;
33        let enum_token = input.parse::<syn::Token![enum]>()?;
34        let ident: syn::Ident = input.parse()?;
35        let generics: syn::Generics = input.parse()?;
36        let where_clause = input.parse()?;
37        let content;
38        let brace_token = syn::braced!(content in input);
39        let variants = content.parse_terminated(EnumDispatchVariant::parse, syn::Token![,])?;
40        Ok(Self {
41            attrs,
42            vis,
43            enum_token,
44            ident,
45            generics: syn::Generics {
46                where_clause,
47                ..generics
48            },
49            brace_token,
50            variants,
51        })
52    }
53}
54
55/// Allows `EnumDispatchItem`s to be converted into `TokenStream`s.
56impl quote::ToTokens for EnumDispatchItem {
57    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
58        tokens.append_all(self.attrs.outer());
59        self.vis.to_tokens(tokens);
60        self.enum_token.to_tokens(tokens);
61        self.ident.to_tokens(tokens);
62        self.generics.to_tokens(tokens);
63        self.generics.where_clause.to_tokens(tokens);
64        self.brace_token.surround(tokens, |tokens| {
65            self.variants.to_tokens(tokens);
66        });
67    }
68}
69
70/// Custom conversion implementation that expands the shorthand `enum_dispatch` enum syntax into a
71/// standard Rust enum syntax.
72impl ::std::convert::From<EnumDispatchItem> for syn::ItemEnum {
73    fn from(item: EnumDispatchItem) -> syn::ItemEnum {
74        use ::std::iter::FromIterator;
75        let variants: Vec<syn::Variant> = item
76            .variants
77            .iter()
78            .map(|variant: &EnumDispatchVariant| syn::Variant {
79                attrs: variant.attrs.to_owned(),
80                ident: variant.ident.to_owned(),
81                fields: syn::Fields::Unnamed(syn::FieldsUnnamed {
82                    paren_token: Default::default(),
83                    unnamed: {
84                        let mut punctuated = syn::punctuated::Punctuated::new();
85                        punctuated.push(syn::Field {
86                            attrs: variant.field_attrs.to_owned(),
87                            vis: syn::Visibility::Inherited,
88                            ident: None,
89                            colon_token: Default::default(),
90                            ty: variant.ty.to_owned(),
91                            mutability: syn::FieldMutability::None,
92                        });
93                        punctuated
94                    },
95                }),
96                discriminant: None,
97            })
98            .collect();
99        syn::ItemEnum {
100            attrs: item.attrs,
101            vis: item.vis,
102            enum_token: item.enum_token,
103            ident: item.ident,
104            generics: syn::Generics {
105                where_clause: item.generics.where_clause,
106                ..item.generics
107            },
108            brace_token: item.brace_token,
109            variants: syn::punctuated::Punctuated::from_iter(variants),
110        }
111    }
112}