1 use proc_macro::TokenStream;
2 use proc_macro2::{Span, TokenStream as TokenStream2};
3 use syn::{parse_macro_input, DeriveInput, Generics, Ident, Type};
4
5 use meta::{self, DataMetaParser, IdentOrIndex, KeyValuePair, MetaParser};
6 use util;
7
8 use super::shared::{self, ConvertDirection};
9
10 use COLOR_TYPES;
11
derive(tokens: TokenStream) -> TokenStream12 pub fn derive(tokens: TokenStream) -> TokenStream {
13 let DeriveInput {
14 ident,
15 attrs,
16 generics: original_generics,
17 data,
18 ..
19 } = parse_macro_input!(tokens);
20 let mut generics = original_generics.clone();
21
22 let mut meta: FromColorMeta = meta::parse_attributes(attrs);
23 let item_meta: FromColorItemMeta = meta::parse_data_attributes(data);
24
25 let (generic_component, generic_white_point) = shared::find_in_generics(
26 meta.component.as_ref(),
27 meta.white_point.as_ref(),
28 &original_generics,
29 );
30
31 let white_point = shared::white_point_type(meta.white_point.clone(), meta.internal);
32 let component = shared::component_type(meta.component.clone());
33
34 let (alpha_property, alpha_type) = item_meta
35 .alpha_property
36 .map(|(property, ty)| (Some(property), ty))
37 .unwrap_or_else(|| (None, component.clone()));
38
39 if generic_component {
40 shared::add_component_where_clause(&component, &mut generics, meta.internal);
41 }
42
43 if generic_white_point {
44 shared::add_white_point_where_clause(&white_point, &mut generics, meta.internal);
45 }
46
47 let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
48
49 // Assume conversion from Xyz by default
50 if meta.manual_implementations.is_empty() {
51 meta.manual_implementations.push(KeyValuePair {
52 key: Ident::new("Xyz", Span::call_site()),
53 value: None,
54 });
55 }
56
57 let methods = shared::generate_methods(
58 &ident,
59 ConvertDirection::From,
60 &meta.manual_implementations,
61 &component,
62 &white_point,
63 &shared::rgb_space_type(meta.rgb_space.clone(), &white_point, meta.internal),
64 &type_generics.as_turbofish(),
65 meta.internal,
66 );
67
68 let from_impls: Vec<_> = COLOR_TYPES
69 .into_iter()
70 .map(|&color| {
71 let skip_regular_from = (meta.internal && ident == color)
72 || meta
73 .manual_implementations
74 .iter()
75 .any(|color_impl| color_impl.key == color && color_impl.value.is_none());
76
77 let regular_from = if skip_regular_from {
78 None
79 } else {
80 Some(impl_from(
81 &ident,
82 color,
83 &meta,
84 &original_generics,
85 generic_component,
86 ))
87 };
88
89 let self_alpha = if meta.internal && ident != color {
90 Some(impl_from_no_alpha_to_alpha(
91 &ident,
92 color,
93 &meta,
94 &original_generics,
95 generic_component,
96 ))
97 } else {
98 None
99 };
100
101 let other_alpha = impl_from_alpha_to_no_alpha(
102 &ident,
103 color,
104 &meta,
105 &original_generics,
106 generic_component,
107 alpha_property.as_ref(),
108 &alpha_type,
109 );
110
111 let both_alpha = if meta.internal && ident != color {
112 Some(impl_from_alpha_to_alpha(
113 &ident,
114 color,
115 &meta,
116 &original_generics,
117 generic_component,
118 ))
119 } else {
120 None
121 };
122
123 quote!{
124 #regular_from
125 #self_alpha
126 #other_alpha
127 #both_alpha
128 }
129 })
130 .collect();
131
132 let trait_path = util::path(&["FromColor"], meta.internal);
133 let from_color_impl = quote!{
134 #[automatically_derived]
135 impl #impl_generics #trait_path<#white_point, #component> for #ident #type_generics #where_clause {
136 #(#methods)*
137 }
138 };
139
140 let result = util::bundle_impl(
141 "FromColor",
142 ident,
143 meta.internal,
144 quote! {
145 #from_color_impl
146 #(#from_impls)*
147 },
148 );
149
150 result.into()
151 }
152
impl_from( ident: &Ident, color: &str, meta: &FromColorMeta, generics: &Generics, generic_component: bool, ) -> TokenStream2153 fn impl_from(
154 ident: &Ident,
155 color: &str,
156 meta: &FromColorMeta,
157 generics: &Generics,
158 generic_component: bool,
159 ) -> TokenStream2 {
160 let (_, type_generics, _) = generics.split_for_impl();
161
162 let FromImplParameters {
163 generics,
164 trait_path,
165 color_ty,
166 method_call,
167 ..
168 } = prepare_from_impl(ident, color, meta, generics, generic_component);
169
170 let (impl_generics, _, where_clause) = generics.split_for_impl();
171
172 quote!{
173 #[automatically_derived]
174 impl #impl_generics From<#color_ty> for #ident #type_generics #where_clause {
175 fn from(color: #color_ty) -> Self {
176 use #trait_path;
177 #method_call
178 }
179 }
180 }
181 }
182
impl_from_alpha_to_alpha( ident: &Ident, color: &str, meta: &FromColorMeta, generics: &Generics, generic_component: bool, ) -> TokenStream2183 fn impl_from_alpha_to_alpha(
184 ident: &Ident,
185 color: &str,
186 meta: &FromColorMeta,
187 generics: &Generics,
188 generic_component: bool,
189 ) -> TokenStream2 {
190 let (_, type_generics, _) = generics.split_for_impl();
191
192 let FromImplParameters {
193 generics,
194 alpha_path,
195 trait_path,
196 color_ty,
197 component,
198 method_call,
199 } = prepare_from_impl(ident, color, meta, generics, generic_component);
200
201 let (impl_generics, _, where_clause) = generics.split_for_impl();
202
203 quote!{
204 #[automatically_derived]
205 impl #impl_generics From<#alpha_path<#color_ty, #component>> for #alpha_path<#ident #type_generics, #component> #where_clause {
206 fn from(color: #alpha_path<#color_ty, #component>) -> Self {
207 use #trait_path;
208 let #alpha_path {color, alpha} = color;
209 #alpha_path {
210 color: #method_call,
211 alpha
212 }
213 }
214 }
215 }
216 }
217
impl_from_no_alpha_to_alpha( ident: &Ident, color: &str, meta: &FromColorMeta, generics: &Generics, generic_component: bool, ) -> TokenStream2218 fn impl_from_no_alpha_to_alpha(
219 ident: &Ident,
220 color: &str,
221 meta: &FromColorMeta,
222 generics: &Generics,
223 generic_component: bool,
224 ) -> TokenStream2 {
225 let (_, type_generics, _) = generics.split_for_impl();
226
227 let FromImplParameters {
228 generics,
229 alpha_path,
230 trait_path,
231 color_ty,
232 method_call,
233 component,
234 } = prepare_from_impl(ident, color, meta, generics, generic_component);
235
236 let (impl_generics, _, where_clause) = generics.split_for_impl();
237
238 quote!{
239 #[automatically_derived]
240 impl #impl_generics From<#color_ty> for #alpha_path<#ident #type_generics, #component> #where_clause {
241 fn from(color: #color_ty) -> Self {
242 use #trait_path;
243 #method_call.into()
244 }
245 }
246 }
247 }
248
impl_from_alpha_to_no_alpha( ident: &Ident, color: &str, meta: &FromColorMeta, generics: &Generics, generic_component: bool, alpha_property: Option<&IdentOrIndex>, alpha_type: &Type, ) -> TokenStream2249 fn impl_from_alpha_to_no_alpha(
250 ident: &Ident,
251 color: &str,
252 meta: &FromColorMeta,
253 generics: &Generics,
254 generic_component: bool,
255 alpha_property: Option<&IdentOrIndex>,
256 alpha_type: &Type,
257 ) -> TokenStream2 {
258 let (_, type_generics, _) = generics.split_for_impl();
259
260 let FromImplParameters {
261 generics,
262 alpha_path,
263 trait_path,
264 color_ty,
265 method_call,
266 ..
267 } = prepare_from_impl(ident, color, meta, generics, generic_component);
268
269 let (impl_generics, _, where_clause) = generics.split_for_impl();
270
271 if let Some(alpha_property) = alpha_property {
272 quote!{
273 #[automatically_derived]
274 impl #impl_generics From<#alpha_path<#color_ty, #alpha_type>> for #ident #type_generics #where_clause {
275 fn from(color: #alpha_path<#color_ty, #alpha_type>) -> Self {
276 use #trait_path;
277 let #alpha_path { color, alpha } = color;
278 let mut result = #method_call;
279 result.#alpha_property = alpha;
280 result
281 }
282 }
283 }
284 } else {
285 quote!{
286 #[automatically_derived]
287 impl #impl_generics From<#alpha_path<#color_ty, #alpha_type>> for #ident #type_generics #where_clause {
288 fn from(color: #alpha_path<#color_ty, #alpha_type>) -> Self {
289 use #trait_path;
290 let color = color.color;
291 #method_call
292 }
293 }
294 }
295 }
296 }
297
prepare_from_impl( ident: &Ident, color: &str, meta: &FromColorMeta, generics: &Generics, generic_component: bool, ) -> FromImplParameters298 fn prepare_from_impl(
299 ident: &Ident,
300 color: &str,
301 meta: &FromColorMeta,
302 generics: &Generics,
303 generic_component: bool,
304 ) -> FromImplParameters {
305 let (_, type_generics, _) = generics.split_for_impl();
306 let turbofish_generics = type_generics.as_turbofish();
307 let mut generics = generics.clone();
308
309 let method_name = Ident::new(&format!("from_{}", color.to_lowercase()), Span::call_site());
310
311 let trait_path = util::path(&["FromColor"], meta.internal);
312 let alpha_path = util::path(&["Alpha"], meta.internal);
313
314 let white_point = shared::white_point_type(meta.white_point.clone(), meta.internal);
315 let component = shared::component_type(meta.component.clone());
316
317 if generic_component {
318 shared::add_component_where_clause(&component, &mut generics, meta.internal)
319 }
320
321 let color_ty = shared::get_convert_color_type(
322 color,
323 &white_point,
324 &component,
325 meta.rgb_space.as_ref(),
326 &mut generics,
327 meta.internal,
328 );
329
330 let method_call = match color {
331 "Rgb" | "Luma" => quote! {
332 #ident #turbofish_generics::#method_name(color.into_linear())
333 },
334 _ => quote! {
335 #ident #turbofish_generics::#method_name(color)
336 },
337 };
338
339 return FromImplParameters {
340 generics,
341 alpha_path,
342 trait_path,
343 color_ty,
344 component,
345 method_call,
346 };
347 }
348
349 struct FromImplParameters {
350 generics: Generics,
351 alpha_path: TokenStream2,
352 trait_path: TokenStream2,
353 color_ty: Type,
354 component: Type,
355 method_call: TokenStream2,
356 }
357
358 #[derive(Default)]
359 struct FromColorMeta {
360 manual_implementations: Vec<KeyValuePair>,
361 internal: bool,
362 component: Option<Type>,
363 white_point: Option<Type>,
364 rgb_space: Option<Type>,
365 }
366
367 impl MetaParser for FromColorMeta {
internal(&mut self)368 fn internal(&mut self) {
369 self.internal = true;
370 }
371
parse_attribute(&mut self, attribute_name: Ident, attribute_tts: TokenStream2)372 fn parse_attribute(&mut self, attribute_name: Ident, attribute_tts: TokenStream2) {
373 match &*attribute_name.to_string() {
374 "palette_manual_from" => {
375 let impls =
376 meta::parse_tuple_attribute::<KeyValuePair>(&attribute_name, attribute_tts);
377 self.manual_implementations.extend(impls)
378 }
379 "palette_component" => {
380 if self.component.is_none() {
381 let component = meta::parse_equal_attribute(&attribute_name, attribute_tts);
382 self.component = Some(component);
383 }
384 }
385 "palette_white_point" => {
386 if self.white_point.is_none() {
387 let white_point = meta::parse_equal_attribute(&attribute_name, attribute_tts);
388 self.white_point = Some(white_point);
389 }
390 }
391 "palette_rgb_space" => {
392 if self.rgb_space.is_none() {
393 let rgb_space = meta::parse_equal_attribute(&attribute_name, attribute_tts);
394 self.rgb_space = Some(rgb_space);
395 }
396 }
397 _ => {}
398 }
399 }
400 }
401
402 #[derive(Default)]
403 struct FromColorItemMeta {
404 alpha_property: Option<(IdentOrIndex, Type)>,
405 }
406
407 impl DataMetaParser for FromColorItemMeta {
parse_struct_field_attribute( &mut self, field_name: IdentOrIndex, ty: Type, attribute_name: Ident, attribute_tts: TokenStream2, )408 fn parse_struct_field_attribute(
409 &mut self,
410 field_name: IdentOrIndex,
411 ty: Type,
412 attribute_name: Ident,
413 attribute_tts: TokenStream2,
414 ) {
415 match &*attribute_name.to_string() {
416 "palette_alpha" => {
417 meta::assert_empty_attribute(&attribute_name, attribute_tts);
418 self.alpha_property = Some((field_name, ty));
419 }
420 _ => {}
421 }
422 }
423 }
424