Skip to main content

strum_macros/helpers/
type_props.rs

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