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
|
use quote::{quote, ToTokens};
use syn::parse::{Parse, ParseStream};
use syn::*;
// An attribute that is a list of idents
pub struct EnumConvertAttribute {
path: Path,
needs_wildcard: bool,
}
impl Parse for EnumConvertAttribute {
fn parse(input: ParseStream) -> Result<Self> {
let paths = input.parse_terminated(Path::parse, Token![,])?;
if paths.is_empty() {
return Err(input.error("#[diplomat::enum_convert] needs a path argument"));
}
let needs_wildcard = if paths.len() == 2 {
if let Some(ident) = paths[1].get_ident() {
if ident == "needs_wildcard" {
true
} else {
return Err(input.error(
"#[diplomat::enum_convert] only recognizes needs_wildcard keyword",
));
}
} else {
return Err(
input.error("#[diplomat::enum_convert] only recognizes needs_wildcard keyword")
);
}
} else if paths.len() > 1 {
return Err(input.error("#[diplomat::enum_convert] only supports up to two arguments"));
} else {
// no needs_wildcard marker
false
};
Ok(EnumConvertAttribute {
path: paths[0].clone(),
needs_wildcard,
})
}
}
pub fn gen_enum_convert(attr: EnumConvertAttribute, input: ItemEnum) -> proc_macro2::TokenStream {
let mut from_arms = vec![];
let mut into_arms = vec![];
let this_name = &input.ident;
let other_name = &attr.path;
for variant in &input.variants {
if variant.fields != Fields::Unit {
return Error::new(variant.ident.span(), "variant may not have fields")
.to_compile_error();
}
let variant_name = &variant.ident;
from_arms.push(quote!(#other_name::#variant_name => Self::#variant_name));
into_arms.push(quote!(#this_name::#variant_name => Self::#variant_name));
}
if attr.needs_wildcard {
let error = format!(
"Encountered unknown field for {}",
other_name.to_token_stream()
);
from_arms.push(quote!(_ => unreachable!(#error)))
}
quote! {
impl From<#other_name> for #this_name {
fn from(other: #other_name) -> Self {
match other {
#(#from_arms,)*
}
}
}
impl From<#this_name> for #other_name {
fn from(this: #this_name) -> Self {
match this {
#(#into_arms,)*
}
}
}
}
}
|