enum_dispatch/
enum_dispatch_variant.rs

1//! Provides an implementation of a `syn`- and `quote`-compatible syntax item describing a single
2//! variant for the shortened enum form used by `enum_dispatch`.
3//!
4//! Each variant can be either just a type, or a name with a single associated tuple type
5//! parameter. In the first form, the name is simply the same as the type. In the second, the name
6//! is explicitly specified.
7
8use std::iter::FromIterator;
9
10use quote::TokenStreamExt;
11
12use crate::filter_attrs::FilterAttrs;
13
14/// A structure that can be used to store syntax information about an `enum_dispatch` enum variant.
15#[derive(Clone)]
16pub struct EnumDispatchVariant {
17    pub attrs: Vec<syn::Attribute>,
18    pub ident: syn::Ident,
19    pub field_attrs: Vec<syn::Attribute>,
20    pub ty: syn::Type,
21}
22
23/// Allows `EnumDispatchItem`s to be parsed from `String`s or `TokenStream`s.
24impl syn::parse::Parse for EnumDispatchVariant {
25    fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
26        let attrs = input.call(syn::Attribute::parse_outer)?;
27        let ident: syn::Ident = input.parse()?;
28        let (field_attrs, ty) = if input.peek(syn::token::Brace) {
29            unimplemented!("enum_dispatch variants cannot have braces for arguments");
30        } else if input.peek(syn::token::Paren) {
31            let input: syn::FieldsUnnamed = input.parse()?;
32            let mut fields = input.unnamed.iter();
33            let field_1 = fields
34                .next()
35                .expect("Named enum_dispatch variants must have one unnamed field");
36            if fields.next().is_some() {
37                panic!("Named enum_dispatch variants can only have one unnamed field");
38            }
39            (field_1.attrs.clone(), field_1.ty.clone())
40        } else {
41            (vec![], into_type(ident.clone()))
42        };
43        Ok(EnumDispatchVariant {
44            attrs,
45            ident,
46            field_attrs,
47            ty,
48        })
49    }
50}
51
52/// Allows `EnumDispatchVariant`s to be converted into `TokenStream`s.
53impl quote::ToTokens for EnumDispatchVariant {
54    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
55        tokens.append_all(self.attrs.outer());
56        self.ident.to_tokens(tokens);
57        syn::token::Paren::default().surround(tokens, |tokens| {
58            tokens.append_all(self.field_attrs.iter());
59            self.ty.to_tokens(tokens);
60        });
61    }
62}
63
64/// When expanding shorthand `enum_dispatch` enum syntax, each specified, unnamed type variant must
65/// acquire an associated identifier to use for the name of the standard Rust enum variant.
66///
67/// Note that `proc_macro_attribute`s cannot provide custom syntax parsing. Unless using a
68/// function-style procedural macro, each type must already be parseable as a unit enum variant.
69/// This rules out, for example, generic types with lifetime or type parameters. For these, an
70/// explicitly named variant must be used.
71fn into_type(ident: syn::Ident) -> syn::Type {
72    syn::Type::Path(syn::TypePath {
73        path: syn::Path {
74            leading_colon: None,
75            segments: syn::punctuated::Punctuated::from_iter(vec![syn::PathSegment {
76                arguments: syn::PathArguments::None,
77                ident,
78            }]),
79        },
80        qself: None,
81    })
82}