• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use quote::{quote, ToTokens};
2 use syn::parse::{Parse, ParseStream};
3 use syn::*;
4 
5 // An attribute that is a list of idents
6 pub struct EnumConvertAttribute {
7     path: Path,
8 
9     needs_wildcard: bool,
10 }
11 
12 impl Parse for EnumConvertAttribute {
parse(input: ParseStream) -> Result<Self>13     fn parse(input: ParseStream) -> Result<Self> {
14         let paths = input.parse_terminated(Path::parse, Token![,])?;
15         if paths.is_empty() {
16             return Err(input.error("#[diplomat::enum_convert] needs a path argument"));
17         }
18         let needs_wildcard = if paths.len() == 2 {
19             if let Some(ident) = paths[1].get_ident() {
20                 if ident == "needs_wildcard" {
21                     true
22                 } else {
23                     return Err(input.error(
24                         "#[diplomat::enum_convert] only recognizes needs_wildcard keyword",
25                     ));
26                 }
27             } else {
28                 return Err(
29                     input.error("#[diplomat::enum_convert] only recognizes needs_wildcard keyword")
30                 );
31             }
32         } else if paths.len() > 1 {
33             return Err(input.error("#[diplomat::enum_convert] only supports up to two arguments"));
34         } else {
35             // no needs_wildcard marker
36             false
37         };
38         Ok(EnumConvertAttribute {
39             path: paths[0].clone(),
40             needs_wildcard,
41         })
42     }
43 }
44 
gen_enum_convert(attr: EnumConvertAttribute, input: ItemEnum) -> proc_macro2::TokenStream45 pub fn gen_enum_convert(attr: EnumConvertAttribute, input: ItemEnum) -> proc_macro2::TokenStream {
46     let mut from_arms = vec![];
47     let mut into_arms = vec![];
48 
49     let this_name = &input.ident;
50     let other_name = &attr.path;
51     for variant in &input.variants {
52         if variant.fields != Fields::Unit {
53             return Error::new(variant.ident.span(), "variant may not have fields")
54                 .to_compile_error();
55         }
56 
57         let variant_name = &variant.ident;
58         from_arms.push(quote!(#other_name::#variant_name => Self::#variant_name));
59         into_arms.push(quote!(#this_name::#variant_name => Self::#variant_name));
60     }
61 
62     if attr.needs_wildcard {
63         let error = format!(
64             "Encountered unknown field for {}",
65             other_name.to_token_stream()
66         );
67         from_arms.push(quote!(_ => unreachable!(#error)))
68     }
69     quote! {
70         impl From<#other_name> for #this_name {
71             fn from(other: #other_name) -> Self {
72                 match other {
73                     #(#from_arms,)*
74                 }
75             }
76         }
77         impl From<#this_name> for #other_name {
78             fn from(this: #this_name) -> Self {
79                 match this {
80                     #(#into_arms,)*
81                 }
82             }
83         }
84     }
85 }
86