strum_macros/helpers/
type_props.rs1use proc_macro2::TokenStream;
2use quote::quote;
3use std::default::Default;
4use syn::{parse_quote, DeriveInput, Ident, LitStr, Path, Visibility};
5
6use super::case_style::CaseStyle;
7use super::metadata::{DeriveInputExt, EnumDiscriminantsMeta, EnumMeta};
8use super::occurrence_error;
9
10pub trait HasTypeProperties {
11 fn get_type_properties(&self) -> syn::Result<StrumTypeProperties>;
12}
13
14#[derive(Clone, Default)]
15pub 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
31impl 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
163impl 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| parse_quote!(#path))
168 }
169}