strum_macros/helpers/
type_props.rs

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