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