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
86
87
88
89
90
91
92
93
94
95
use proc_macro2::{Span, TokenStream};
use quote::{quote, ToTokens};
use syn::{Fields, FieldsNamed, FieldsUnnamed, ItemEnum};
use super::{
attr::EnumAttrs,
util::{get_enum_attributes, get_rename_rule},
};
pub fn expand_enum_from_string(input: &ItemEnum) -> syn::Result<TokenStream> {
let enum_name = &input.ident;
let rename_rule = get_rename_rule(input)?;
let mut fallback = None;
let branches: Vec<_> = input
.variants
.iter()
.map(|v| {
let variant_name = &v.ident;
let EnumAttrs { rename, aliases } = get_enum_attributes(v)?;
let variant_str = match (rename, &v.fields) {
(None, Fields::Unit) => Some(
rename_rule.apply_to_variant(&variant_name.to_string()).into_token_stream(),
),
(Some(rename), Fields::Unit) => Some(rename.into_token_stream()),
(None, Fields::Named(FieldsNamed { named: fields, .. }))
| (None, Fields::Unnamed(FieldsUnnamed { unnamed: fields, .. })) => {
if fields.len() != 1 {
return Err(syn::Error::new_spanned(
v,
"multiple data fields are not supported",
));
}
if fallback.is_some() {
return Err(syn::Error::new_spanned(
v,
"multiple data-carrying variants are not supported",
));
}
let member = match &fields[0].ident {
Some(name) => name.into_token_stream(),
None => quote! { 0 },
};
let ty = &fields[0].ty;
fallback = Some(quote! {
_ => #enum_name::#variant_name {
#member: #ty(s.into()),
}
});
None
}
(Some(_), _) => {
return Err(syn::Error::new_spanned(
v,
"ruma_enum(rename) is only allowed on unit variants",
));
}
};
Ok(variant_str.map(|s| {
quote! {
#( #aliases => #enum_name :: #variant_name, )*
#s => #enum_name :: #variant_name
}
}))
})
.collect::<syn::Result<_>>()?;
let branches = branches.iter().flatten();
if fallback.is_none() {
return Err(syn::Error::new(Span::call_site(), "required fallback variant not found"));
}
Ok(quote! {
#[automatically_derived]
#[allow(deprecated)]
impl<T> ::std::convert::From<T> for #enum_name
where
T: ::std::convert::AsRef<::std::primitive::str>
+ ::std::convert::Into<::std::boxed::Box<::std::primitive::str>>
{
fn from(s: T) -> Self {
match s.as_ref() {
#( #branches, )*
#fallback
}
}
}
})
}