enum_dispatch/
enum_dispatch_item.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
//! Provides an implementation of a `syn`- and `quote`-compatible syntax item describing the
//! shortened enum form used by `enum_dispatch`.
//!
//! The syntax is *mostly* identical to that of standard enums. The only difference is the
//! specification of enum variants -- in the custom `EnumDispatchItem` type, each variant must be
//! specified as a `syn::Type` rather than a `syn::Variant`. In the case of basic unit fields named
//! after existing scoped types, a normal Rust enum can be parsed as an EnumDispatchItem without
//! issue.
use quote::TokenStreamExt;

use crate::enum_dispatch_variant::EnumDispatchVariant;
use crate::filter_attrs::FilterAttrs;

/// A structure that can be used to store syntax information about an `enum_dispatch` enum.
///
/// Mostly identical to `syn::ItemEnum`.
#[derive(Clone)]
pub struct EnumDispatchItem {
    pub attrs: Vec<syn::Attribute>,
    pub vis: syn::Visibility,
    enum_token: syn::token::Enum,
    pub ident: syn::Ident,
    pub generics: syn::Generics,
    brace_token: syn::token::Brace,
    pub variants: syn::punctuated::Punctuated<EnumDispatchVariant, syn::token::Comma>,
}

/// Allows `EnumDispatchItem`s to be parsed from `String`s or `TokenStream`s.
impl syn::parse::Parse for EnumDispatchItem {
    fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
        let attrs = input.call(syn::Attribute::parse_outer)?;
        let vis: syn::Visibility = input.parse()?;
        let enum_token = input.parse::<syn::Token![enum]>()?;
        let ident: syn::Ident = input.parse()?;
        let generics: syn::Generics = input.parse()?;
        let where_clause = input.parse()?;
        let content;
        let brace_token = syn::braced!(content in input);
        let variants = content.parse_terminated(EnumDispatchVariant::parse, syn::Token![,])?;
        Ok(Self {
            attrs,
            vis,
            enum_token,
            ident,
            generics: syn::Generics {
                where_clause,
                ..generics
            },
            brace_token,
            variants,
        })
    }
}

/// Allows `EnumDispatchItem`s to be converted into `TokenStream`s.
impl quote::ToTokens for EnumDispatchItem {
    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
        tokens.append_all(self.attrs.outer());
        self.vis.to_tokens(tokens);
        self.enum_token.to_tokens(tokens);
        self.ident.to_tokens(tokens);
        self.generics.to_tokens(tokens);
        self.generics.where_clause.to_tokens(tokens);
        self.brace_token.surround(tokens, |tokens| {
            self.variants.to_tokens(tokens);
        });
    }
}

/// Custom conversion implementation that expands the shorthand `enum_dispatch` enum syntax into a
/// standard Rust enum syntax.
impl ::std::convert::From<EnumDispatchItem> for syn::ItemEnum {
    fn from(item: EnumDispatchItem) -> syn::ItemEnum {
        use ::std::iter::FromIterator;
        let variants: Vec<syn::Variant> = item
            .variants
            .iter()
            .map(|variant: &EnumDispatchVariant| syn::Variant {
                attrs: variant.attrs.to_owned(),
                ident: variant.ident.to_owned(),
                fields: syn::Fields::Unnamed(syn::FieldsUnnamed {
                    paren_token: Default::default(),
                    unnamed: {
                        let mut punctuated = syn::punctuated::Punctuated::new();
                        punctuated.push(syn::Field {
                            attrs: variant.field_attrs.to_owned(),
                            vis: syn::Visibility::Inherited,
                            ident: None,
                            colon_token: Default::default(),
                            ty: variant.ty.to_owned(),
                            mutability: syn::FieldMutability::None,
                        });
                        punctuated
                    },
                }),
                discriminant: None,
            })
            .collect();
        syn::ItemEnum {
            attrs: item.attrs,
            vis: item.vis,
            enum_token: item.enum_token,
            ident: item.ident,
            generics: syn::Generics {
                where_clause: item.generics.where_clause,
                ..item.generics
            },
            brace_token: item.brace_token,
            variants: syn::punctuated::Punctuated::from_iter(variants),
        }
    }
}