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.
78use std::iter::FromIterator;
910use quote::TokenStreamExt;
1112use crate::filter_attrs::FilterAttrs;
1314/// A structure that can be used to store syntax information about an `enum_dispatch` enum variant.
15#[derive(Clone)]
16pub struct EnumDispatchVariant {
17pub attrs: Vec<syn::Attribute>,
18pub ident: syn::Ident,
19pub field_attrs: Vec<syn::Attribute>,
20pub ty: syn::Type,
21}
2223/// Allows `EnumDispatchItem`s to be parsed from `String`s or `TokenStream`s.
24impl syn::parse::Parse for EnumDispatchVariant {
25fn parse(input: syn::parse::ParseStream) -> syn::parse::Result<Self> {
26let attrs = input.call(syn::Attribute::parse_outer)?;
27let ident: syn::Ident = input.parse()?;
28let (field_attrs, ty) = if input.peek(syn::token::Brace) {
29unimplemented!("enum_dispatch variants cannot have braces for arguments");
30 } else if input.peek(syn::token::Paren) {
31let input: syn::FieldsUnnamed = input.parse()?;
32let mut fields = input.unnamed.iter();
33let field_1 = fields
34 .next()
35 .expect("Named enum_dispatch variants must have one unnamed field");
36if fields.next().is_some() {
37panic!("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 };
43Ok(EnumDispatchVariant {
44 attrs,
45 ident,
46 field_attrs,
47 ty,
48 })
49 }
50}
5152/// Allows `EnumDispatchVariant`s to be converted into `TokenStream`s.
53impl quote::ToTokens for EnumDispatchVariant {
54fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
55 tokens.append_all(self.attrs.outer());
56self.ident.to_tokens(tokens);
57 syn::token::Paren::default().surround(tokens, |tokens| {
58 tokens.append_all(self.field_attrs.iter());
59self.ty.to_tokens(tokens);
60 });
61 }
62}
6364/// 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}