educe/trait_handlers/default/models/
field_attribute.rs

1use quote::ToTokens;
2use syn::{Attribute, Lit, Meta, NestedMeta};
3
4use super::super::super::create_expr_string_from_lit_str;
5use crate::{panic, Trait};
6
7#[derive(Clone)]
8pub struct FieldAttribute {
9    pub flag:       bool,
10    pub literal:    Option<Lit>,
11    pub expression: Option<String>,
12}
13
14#[derive(Debug, Clone)]
15pub struct FieldAttributeBuilder {
16    pub enable_flag:       bool,
17    pub enable_literal:    bool,
18    pub enable_expression: bool,
19}
20
21impl FieldAttributeBuilder {
22    #[allow(clippy::wrong_self_convention)]
23    pub fn from_default_meta(&self, meta: &Meta) -> FieldAttribute {
24        let mut flag = false;
25        let mut value: Option<Lit> = None;
26        let mut expression: Option<String> = None;
27
28        let correct_usage_for_default_attribute = {
29            let mut usage = vec![];
30
31            if self.enable_flag {
32                usage.push(stringify!(#[educe(Default)]));
33            }
34
35            if self.enable_literal {
36                usage.push(stringify!(#[educe(Default = literal)]));
37                usage.push(stringify!(#[educe(Default(literal))]));
38            }
39
40            usage
41        };
42
43        let correct_usage_for_expression = {
44            let usage = vec![
45                stringify!(#[educe(Default(expression = "expression"))]),
46                stringify!(#[educe(Default(expression("expression")))]),
47            ];
48
49            usage
50        };
51
52        match meta {
53            Meta::List(list) => {
54                for p in list.nested.iter() {
55                    match p {
56                        NestedMeta::Meta(meta) => {
57                            let meta_name = meta.path().into_token_stream().to_string();
58
59                            match meta_name.as_str() {
60                                "expression" | "expr" => {
61                                    if !self.enable_expression {
62                                        panic::unknown_parameter("Default", meta_name.as_str());
63                                    }
64
65                                    match meta {
66                                        Meta::List(list) => {
67                                            for p in list.nested.iter() {
68                                                match p {
69                                                    NestedMeta::Lit(Lit::Str(s)) => {
70                                                        if expression.is_some() {
71                                                            panic::reset_parameter(
72                                                                meta_name.as_str(),
73                                                            );
74                                                        }
75
76                                                        let s = create_expr_string_from_lit_str(s);
77
78                                                        if s.is_some() {
79                                                            expression = s;
80                                                        } else {
81                                                            panic::empty_parameter(
82                                                                meta_name.as_str(),
83                                                            )
84                                                        }
85                                                    },
86                                                    _ => panic::parameter_incorrect_format(
87                                                        meta_name.as_str(),
88                                                        &correct_usage_for_expression,
89                                                    ),
90                                                }
91                                            }
92                                        },
93                                        Meta::NameValue(named_value) => {
94                                            let lit = &named_value.lit;
95
96                                            match lit {
97                                                Lit::Str(s) => {
98                                                    if expression.is_some() {
99                                                        panic::reset_parameter(meta_name.as_str());
100                                                    }
101
102                                                    let s = create_expr_string_from_lit_str(s);
103
104                                                    if s.is_some() {
105                                                        expression = s;
106                                                    } else {
107                                                        panic::empty_parameter(meta_name.as_str())
108                                                    }
109                                                },
110                                                _ => panic::parameter_incorrect_format(
111                                                    meta_name.as_str(),
112                                                    &correct_usage_for_expression,
113                                                ),
114                                            }
115                                        },
116                                        _ => panic::parameter_incorrect_format(
117                                            meta_name.as_str(),
118                                            &correct_usage_for_expression,
119                                        ),
120                                    }
121                                },
122                                _ => panic::unknown_parameter("Default", meta_name.as_str()),
123                            }
124                        },
125                        NestedMeta::Lit(lit) => {
126                            if !self.enable_literal {
127                                panic::attribute_incorrect_format(
128                                    "Default",
129                                    &correct_usage_for_default_attribute,
130                                )
131                            }
132
133                            if value.is_some() {
134                                panic::reset_parameter("value");
135                            }
136
137                            value = Some(lit.clone());
138                        },
139                    }
140                }
141            },
142            Meta::NameValue(named_value) => {
143                if !self.enable_literal {
144                    panic::attribute_incorrect_format(
145                        "Default",
146                        &correct_usage_for_default_attribute,
147                    )
148                }
149
150                let lit = &named_value.lit;
151
152                value = Some(lit.clone());
153            },
154            Meta::Path(_) => {
155                if !self.enable_flag {
156                    panic::attribute_incorrect_format(
157                        "Default",
158                        &correct_usage_for_default_attribute,
159                    );
160                }
161
162                flag = true;
163            },
164        }
165
166        if value.is_some() && expression.is_some() {
167            panic::set_value_expression();
168        }
169
170        FieldAttribute {
171            flag,
172            literal: value,
173            expression,
174        }
175    }
176
177    #[allow(clippy::wrong_self_convention)]
178    pub fn from_attributes(self, attributes: &[Attribute], traits: &[Trait]) -> FieldAttribute {
179        let mut result = None;
180
181        for attribute in attributes.iter() {
182            if attribute.path.is_ident("educe") {
183                let meta = attribute.parse_meta().unwrap();
184
185                match meta {
186                    Meta::List(list) => {
187                        for p in list.nested.iter() {
188                            match p {
189                                NestedMeta::Meta(meta) => {
190                                    let meta_name = meta.path().into_token_stream().to_string();
191
192                                    let t = Trait::from_str(meta_name);
193
194                                    if traits.binary_search(&t).is_err() {
195                                        panic::trait_not_used(t);
196                                    }
197
198                                    if t == Trait::Default {
199                                        if result.is_some() {
200                                            panic::reuse_a_trait(t);
201                                        }
202
203                                        result = Some(self.from_default_meta(meta));
204                                    }
205                                },
206                                _ => panic::educe_format_incorrect(),
207                            }
208                        }
209                    },
210                    _ => panic::educe_format_incorrect(),
211                }
212            }
213        }
214
215        result.unwrap_or(FieldAttribute {
216            flag: false, literal: None, expression: None
217        })
218    }
219}