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