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 (Debug, Clone, Default)] |
15 | pub struct StrumTypeProperties { |
16 | pub case_style: Option<CaseStyle>, |
17 | pub ascii_case_insensitive: bool, |
18 | pub crate_module_path: Option<Path>, |
19 | pub discriminant_derives: Vec<Path>, |
20 | pub discriminant_name: Option<Ident>, |
21 | pub discriminant_others: Vec<TokenStream>, |
22 | pub discriminant_vis: Option<Visibility>, |
23 | pub use_phf: bool, |
24 | pub prefix: Option<LitStr>, |
25 | pub enum_repr: Option<TokenStream>, |
26 | } |
27 | |
28 | impl HasTypeProperties for DeriveInput { |
29 | fn get_type_properties(&self) -> syn::Result<StrumTypeProperties> { |
30 | let mut output = StrumTypeProperties::default(); |
31 | |
32 | let strum_meta = self.get_metadata()?; |
33 | let discriminants_meta = self.get_discriminants_metadata()?; |
34 | |
35 | let mut serialize_all_kw = None; |
36 | let mut ascii_case_insensitive_kw = None; |
37 | let mut use_phf_kw = None; |
38 | let mut crate_module_path_kw = None; |
39 | let mut prefix_kw = None; |
40 | for meta in strum_meta { |
41 | match meta { |
42 | EnumMeta::SerializeAll { case_style, kw } => { |
43 | if let Some(fst_kw) = serialize_all_kw { |
44 | return Err(occurrence_error(fst_kw, kw, "serialize_all" )); |
45 | } |
46 | |
47 | serialize_all_kw = Some(kw); |
48 | output.case_style = Some(case_style); |
49 | } |
50 | EnumMeta::AsciiCaseInsensitive(kw) => { |
51 | if let Some(fst_kw) = ascii_case_insensitive_kw { |
52 | return Err(occurrence_error(fst_kw, kw, "ascii_case_insensitive" )); |
53 | } |
54 | |
55 | ascii_case_insensitive_kw = Some(kw); |
56 | output.ascii_case_insensitive = true; |
57 | } |
58 | EnumMeta::UsePhf(kw) => { |
59 | if let Some(fst_kw) = use_phf_kw { |
60 | return Err(occurrence_error(fst_kw, kw, "use_phf" )); |
61 | } |
62 | |
63 | use_phf_kw = Some(kw); |
64 | output.use_phf = true; |
65 | } |
66 | EnumMeta::Crate { |
67 | crate_module_path, |
68 | kw, |
69 | } => { |
70 | if let Some(fst_kw) = crate_module_path_kw { |
71 | return Err(occurrence_error(fst_kw, kw, "Crate" )); |
72 | } |
73 | |
74 | crate_module_path_kw = Some(kw); |
75 | output.crate_module_path = Some(crate_module_path); |
76 | } |
77 | EnumMeta::Prefix { prefix, kw } => { |
78 | if let Some(fst_kw) = prefix_kw { |
79 | return Err(occurrence_error(fst_kw, kw, "prefix" )); |
80 | } |
81 | |
82 | prefix_kw = Some(kw); |
83 | output.prefix = Some(prefix); |
84 | } |
85 | } |
86 | } |
87 | |
88 | let mut name_kw = None; |
89 | let mut vis_kw = None; |
90 | for meta in discriminants_meta { |
91 | match meta { |
92 | EnumDiscriminantsMeta::Derive { paths, .. } => { |
93 | output.discriminant_derives.extend(paths); |
94 | } |
95 | EnumDiscriminantsMeta::Name { name, kw } => { |
96 | if let Some(fst_kw) = name_kw { |
97 | return Err(occurrence_error(fst_kw, kw, "name" )); |
98 | } |
99 | |
100 | name_kw = Some(kw); |
101 | output.discriminant_name = Some(name); |
102 | } |
103 | EnumDiscriminantsMeta::Vis { vis, kw } => { |
104 | if let Some(fst_kw) = vis_kw { |
105 | return Err(occurrence_error(fst_kw, kw, "vis" )); |
106 | } |
107 | |
108 | vis_kw = Some(kw); |
109 | output.discriminant_vis = Some(vis); |
110 | } |
111 | EnumDiscriminantsMeta::Other { path, nested } => { |
112 | output.discriminant_others.push(quote! { #path(#nested) }); |
113 | } |
114 | } |
115 | } |
116 | |
117 | let attrs = &self.attrs; |
118 | for attr in attrs { |
119 | if let Ok(list) = attr.meta.require_list() { |
120 | if let Some(ident) = list.path.get_ident() { |
121 | if ident == "repr" { |
122 | output.enum_repr = Some(list.tokens.clone()) |
123 | } |
124 | } |
125 | } |
126 | } |
127 | |
128 | Ok(output) |
129 | } |
130 | } |
131 | |
132 | impl StrumTypeProperties { |
133 | pub fn crate_module_path(&self) -> Path { |
134 | self.crate_module_path |
135 | .as_ref() |
136 | .map_or_else(|| parse_quote!(::strum), |path: &Path| parse_quote!(#path)) |
137 | } |
138 | } |
139 | |