1 | use proc_macro2::TokenStream; |
2 | use quote::quote; |
3 | use std::default::Default; |
4 | use syn::{parse_quote, DeriveInput, Ident, LitStr, Path, Visibility}; |
5 | |
6 | use super::case_style::CaseStyle; |
7 | use super::metadata::{DeriveInputExt, EnumDiscriminantsMeta, EnumMeta}; |
8 | use super::occurrence_error; |
9 | |
10 | pub trait HasTypeProperties { |
11 | fn get_type_properties(&self) -> syn::Result<StrumTypeProperties>; |
12 | } |
13 | |
14 | #[derive (Clone, Default)] |
15 | pub struct StrumTypeProperties { |
16 | pub parse_err_ty: Option<Path>, |
17 | pub parse_err_fn: Option<Path>, |
18 | pub case_style: Option<CaseStyle>, |
19 | pub ascii_case_insensitive: bool, |
20 | pub crate_module_path: Option<Path>, |
21 | pub discriminant_derives: Vec<Path>, |
22 | pub discriminant_name: Option<Ident>, |
23 | pub discriminant_others: Vec<TokenStream>, |
24 | pub discriminant_vis: Option<Visibility>, |
25 | pub use_phf: bool, |
26 | pub prefix: Option<LitStr>, |
27 | pub enum_repr: Option<TokenStream>, |
28 | pub const_into_str: bool, |
29 | } |
30 | |
31 | impl HasTypeProperties for DeriveInput { |
32 | fn get_type_properties(&self) -> syn::Result<StrumTypeProperties> { |
33 | let mut output = StrumTypeProperties::default(); |
34 | |
35 | let strum_meta = self.get_metadata()?; |
36 | let discriminants_meta = self.get_discriminants_metadata()?; |
37 | |
38 | let mut parse_err_ty_kw = None; |
39 | let mut parse_err_fn_kw = None; |
40 | let mut serialize_all_kw = None; |
41 | let mut ascii_case_insensitive_kw = None; |
42 | let mut use_phf_kw = None; |
43 | let mut crate_module_path_kw = None; |
44 | let mut prefix_kw = None; |
45 | let mut const_into_str = None; |
46 | |
47 | for meta in strum_meta { |
48 | match meta { |
49 | EnumMeta::SerializeAll { case_style, kw } => { |
50 | if let Some(fst_kw) = serialize_all_kw { |
51 | return Err(occurrence_error(fst_kw, kw, "serialize_all" )); |
52 | } |
53 | |
54 | serialize_all_kw = Some(kw); |
55 | output.case_style = Some(case_style); |
56 | } |
57 | EnumMeta::AsciiCaseInsensitive(kw) => { |
58 | if let Some(fst_kw) = ascii_case_insensitive_kw { |
59 | return Err(occurrence_error(fst_kw, kw, "ascii_case_insensitive" )); |
60 | } |
61 | |
62 | ascii_case_insensitive_kw = Some(kw); |
63 | output.ascii_case_insensitive = true; |
64 | } |
65 | EnumMeta::UsePhf(kw) => { |
66 | if let Some(fst_kw) = use_phf_kw { |
67 | return Err(occurrence_error(fst_kw, kw, "use_phf" )); |
68 | } |
69 | |
70 | use_phf_kw = Some(kw); |
71 | output.use_phf = true; |
72 | } |
73 | EnumMeta::Crate { |
74 | crate_module_path, |
75 | kw, |
76 | } => { |
77 | if let Some(fst_kw) = crate_module_path_kw { |
78 | return Err(occurrence_error(fst_kw, kw, "Crate" )); |
79 | } |
80 | |
81 | crate_module_path_kw = Some(kw); |
82 | output.crate_module_path = Some(crate_module_path); |
83 | } |
84 | EnumMeta::Prefix { prefix, kw } => { |
85 | if let Some(fst_kw) = prefix_kw { |
86 | return Err(occurrence_error(fst_kw, kw, "prefix" )); |
87 | } |
88 | |
89 | prefix_kw = Some(kw); |
90 | output.prefix = Some(prefix); |
91 | } |
92 | EnumMeta::ParseErrTy { path, kw } => { |
93 | if let Some(fst_kw) = parse_err_ty_kw { |
94 | return Err(occurrence_error(fst_kw, kw, "parse_err_ty" )); |
95 | } |
96 | |
97 | parse_err_ty_kw = Some(kw); |
98 | output.parse_err_ty = Some(path); |
99 | } |
100 | EnumMeta::ParseErrFn { path, kw } => { |
101 | if let Some(fst_kw) = parse_err_fn_kw { |
102 | return Err(occurrence_error(fst_kw, kw, "parse_err_fn" )); |
103 | } |
104 | |
105 | parse_err_fn_kw = Some(kw); |
106 | output.parse_err_fn = Some(path); |
107 | } |
108 | EnumMeta::ConstIntoStr(kw) => { |
109 | if let Some(fst_kw) = const_into_str { |
110 | return Err(occurrence_error(fst_kw, kw, "const_into_str" )); |
111 | } |
112 | |
113 | const_into_str = Some(kw); |
114 | output.const_into_str = true; |
115 | } |
116 | } |
117 | } |
118 | |
119 | let mut name_kw = None; |
120 | let mut vis_kw = None; |
121 | for meta in discriminants_meta { |
122 | match meta { |
123 | EnumDiscriminantsMeta::Derive { paths, .. } => { |
124 | output.discriminant_derives.extend(paths); |
125 | } |
126 | EnumDiscriminantsMeta::Name { name, kw } => { |
127 | if let Some(fst_kw) = name_kw { |
128 | return Err(occurrence_error(fst_kw, kw, "name" )); |
129 | } |
130 | |
131 | name_kw = Some(kw); |
132 | output.discriminant_name = Some(name); |
133 | } |
134 | EnumDiscriminantsMeta::Vis { vis, kw } => { |
135 | if let Some(fst_kw) = vis_kw { |
136 | return Err(occurrence_error(fst_kw, kw, "vis" )); |
137 | } |
138 | |
139 | vis_kw = Some(kw); |
140 | output.discriminant_vis = Some(vis); |
141 | } |
142 | EnumDiscriminantsMeta::Other { path, nested } => { |
143 | output.discriminant_others.push(quote! { #path(#nested) }); |
144 | } |
145 | } |
146 | } |
147 | |
148 | let attrs = &self.attrs; |
149 | for attr in attrs { |
150 | if let Ok(list) = attr.meta.require_list() { |
151 | if let Some(ident) = list.path.get_ident() { |
152 | if ident == "repr" { |
153 | output.enum_repr = Some(list.tokens.clone()) |
154 | } |
155 | } |
156 | } |
157 | } |
158 | |
159 | Ok(output) |
160 | } |
161 | } |
162 | |
163 | impl StrumTypeProperties { |
164 | pub fn crate_module_path(&self) -> Path { |
165 | self.crate_module_path |
166 | .as_ref() |
167 | .map_or_else(|| parse_quote!(::strum), |path: &Path| parse_quote!(#path)) |
168 | } |
169 | } |
170 | |