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