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