1use std::collections::BTreeMap;
2
3use failure::format_err;
4use proc_macro2::TokenStream;
5use quote::quote;
6
7use crate::attr::{Enum, ErrorList};
8use enum_utils_from_str::{Case, StrMapFunc};
9
10struct FromStrImpl {
11 nocase: bool,
12 enum_name: syn::Ident,
13 variants: BTreeMap<String, syn::Ident>,
14}
15
16impl FromStrImpl {
17 pub fn parse(input: &syn::DeriveInput) -> Result<Self, ErrorList> {
18 let Enum { name, attrs: enum_attrs, variants, .. } = Enum::parse(input)?;
19
20 let mut errors = ErrorList::default();
21 let mut name_map = BTreeMap::default();
22 for (v, attrs) in variants.iter() {
23 if attrs.skip {
24 continue;
25 }
26
27 if v.fields != syn::Fields::Unit {
28 errors.push_back(format_err!("An (unskipped) variant cannot have fields"));
29 }
30
31 if let Some(name) = &attrs.rename {
32 name_map.insert(name.clone(), v.ident.clone());
33 } else if let Some(rename_rule) = &enum_attrs.rename_rule {
34 let s = v.ident.to_string();
35 name_map.insert(rename_rule.apply_to_variant(&*s), v.ident.clone());
36 } else {
37 let s = v.ident.to_string();
38 name_map.insert(s, v.ident.clone());
39 }
40
41 for alias in &attrs.aliases {
42 name_map.insert(alias.clone(), v.ident.clone());
43 }
44 }
45
46 if !errors.is_empty() {
47 return Err(errors);
48 }
49
50 Ok(FromStrImpl {
51 nocase: enum_attrs.nocase,
52 enum_name: name.clone(),
53 variants: name_map,
54 })
55 }
56}
57
58pub fn derive(ast: &syn::DeriveInput) -> Result<TokenStream, ErrorList> {
59 let FromStrImpl { nocase, enum_name, variants } = FromStrImpl::parse(ast)?;
60
61 let mut trie = StrMapFunc::new("_parse", &enum_name.to_string());
62 let case = if nocase { Case::Insensitive } else { Case::Sensitive };
63 trie.case(case);
64
65 for (alias, variant) in variants {
66 let path = quote!(#enum_name::#variant);
67 trie.entry(alias.as_str(), path);
68 }
69
70 Ok(quote!{
71 impl ::std::str::FromStr for #enum_name {
72 type Err = ();
73
74 fn from_str(s: &str) -> Result<Self, Self::Err> {
75 #trie
76 _parse(s.as_bytes()).ok_or(())
77 }
78 }
79 })
80}