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(Debug, Clone, Default)]
15pub 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
28impl 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
132impl 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| parse_quote!(#path))
137    }
138}