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 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}