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}