1use proc_macro::TokenStream;
2use proc_macro2::{Span, TokenStream as TokenStream2};
3use quote::quote;
4use syn::{parse_quote, DeriveInput, Generics, Ident, Result, Type};
5
6use crate::{
7 color_types::{ColorInfo, MetaTypeSource, XYZ_COLORS},
8 convert::util::{InputUser, WhitePointSource},
9 meta::{
10 parse_field_attributes, parse_namespaced_attributes, FieldAttributes, IdentOrIndex,
11 TypeItemAttributes,
12 },
13 util,
14};
15
16use super::util::{component_type, find_nearest_color, get_convert_color_type, white_point_type};
17
18pub fn derive(item: TokenStream) -> ::std::result::Result<TokenStream, Vec<::syn::parse::Error>> {
19 let DeriveInput {
20 ident,
21 generics,
22 data,
23 attrs,
24 ..
25 } = syn::parse(item).map_err(|error| vec![error])?;
26
27 let (mut item_meta, item_errors) = parse_namespaced_attributes::<TypeItemAttributes>(attrs);
28
29 let (fields_meta, field_errors) = if let syn::Data::Struct(struct_data) = data {
30 parse_field_attributes::<FieldAttributes>(struct_data.fields)
31 } else {
32 return Err(vec![syn::Error::new(
33 Span::call_site(),
34 "only structs are supported",
35 )]);
36 };
37
38 let component = component_type(item_meta.component.clone());
39 let white_point = white_point_type(
40 item_meta.white_point.as_ref(),
41 item_meta.rgb_standard.as_ref(),
42 item_meta.luma_standard.as_ref(),
43 item_meta.internal,
44 );
45
46 let alpha_field = fields_meta.alpha_property;
47
48 if item_meta.color_groups.is_empty() {
50 item_meta.color_groups.insert((&XYZ_COLORS).into());
51 }
52
53 if item_meta.skip_derives.is_empty() {
54 for group in &item_meta.color_groups {
55 item_meta.skip_derives.insert(group.root_type.name.into());
56 }
57 }
58
59 let (all_from_impl_params, impl_params_errors) =
60 prepare_from_impl(&component, white_point, &item_meta, &generics);
61
62 let mut implementations =
63 generate_from_implementations(&ident, &generics, &item_meta, &all_from_impl_params);
64
65 if let Some((alpha_property, alpha_type)) = alpha_field {
66 implementations.push(generate_from_alpha_implementation_with_internal(
67 &ident,
68 &generics,
69 &item_meta,
70 &alpha_property,
71 &alpha_type,
72 ));
73 } else {
74 implementations.push(generate_from_alpha_implementation(
75 &ident, &generics, &item_meta,
76 ));
77 }
78
79 let item_errors = item_errors
80 .into_iter()
81 .map(|error| error.into_compile_error());
82 let field_errors = field_errors
83 .into_iter()
84 .map(|error| error.into_compile_error());
85 let impl_params_errors = impl_params_errors
86 .into_iter()
87 .map(|error| error.into_compile_error());
88
89 Ok(quote! {
90 #(#item_errors)*
91 #(#field_errors)*
92 #(#impl_params_errors)*
93
94 #(#implementations)*
95 }
96 .into())
97}
98
99fn prepare_from_impl(
100 component: &Type,
101 white_point: Option<(Type, WhitePointSource)>,
102 meta: &TypeItemAttributes,
103 generics: &Generics,
104) -> (Vec<FromImplParameters>, Vec<syn::Error>) {
105 let included_colors = meta
106 .color_groups
107 .iter()
108 .flat_map(|group| group.color_names())
109 .filter(|&color| !meta.skip_derives.contains(color.name));
110
111 let mut parameters = Vec::new();
112 let mut errors = Vec::new();
113
114 for color in included_colors {
115 let impl_params = prepare_from_impl_for_pair(
116 color,
117 component,
118 white_point.clone(),
119 meta,
120 generics.clone(),
121 );
122
123 match impl_params {
124 Ok(Some(impl_params)) => parameters.push(impl_params),
125 Ok(None) => {}
126 Err(error) => errors.push(error),
127 }
128 }
129
130 (parameters, errors)
131}
132
133fn prepare_from_impl_for_pair(
134 color: &ColorInfo,
135 component: &Type,
136 white_point: Option<(Type, WhitePointSource)>,
137 meta: &TypeItemAttributes,
138 mut generics: Generics,
139) -> Result<Option<FromImplParameters>> {
140 let nearest_color = find_nearest_color(color, meta)?;
141
142 let (white_point, white_point_source) = if let Some((white_point, source)) = white_point {
144 (white_point, source)
145 } else {
146 color.get_default_white_point(meta.internal)
147 };
148
149 let (color_ty, mut used_input) =
150 get_convert_color_type(color, &white_point, component, meta, &mut generics)?;
151
152 let nearest_color_ty = nearest_color.get_type(
153 MetaTypeSource::OtherColor(color),
154 component,
155 &white_point,
156 &mut used_input,
157 InputUser::Nearest,
158 meta,
159 )?;
160
161 if used_input.white_point.is_unconstrained()
165 && matches!(white_point_source, WhitePointSource::GeneratedGeneric)
166 {
167 return Ok(None);
168 }
169
170 if used_input.white_point.is_used() {
171 match white_point_source {
172 WhitePointSource::WhitePoint => {
173 let white_point_path = util::path(["white_point", "WhitePoint"], meta.internal);
174 generics
175 .make_where_clause()
176 .predicates
177 .push(parse_quote!(#white_point: #white_point_path<#component>))
178 }
179 WhitePointSource::RgbStandard => {
180 let rgb_standard_path = util::path(["rgb", "RgbStandard"], meta.internal);
181 let rgb_standard = meta.rgb_standard.as_ref();
182 generics
183 .make_where_clause()
184 .predicates
185 .push(parse_quote!(#rgb_standard: #rgb_standard_path));
186 }
187 WhitePointSource::LumaStandard => {
188 let luma_standard_path = util::path(["luma", "LumaStandard"], meta.internal);
189 let luma_standard = meta.luma_standard.as_ref();
190 generics
191 .make_where_clause()
192 .predicates
193 .push(parse_quote!(#luma_standard: #luma_standard_path));
194 }
195 WhitePointSource::ConcreteType => {}
196 WhitePointSource::GeneratedGeneric => {
197 generics.params.push(parse_quote!(_Wp));
198 }
199 }
200 }
201
202 Ok(Some(FromImplParameters {
203 generics,
204 color_ty,
205 nearest_color_ty,
206 }))
207}
208
209struct FromImplParameters {
210 generics: Generics,
211 color_ty: Type,
212 nearest_color_ty: Type,
213}
214
215fn generate_from_implementations(
216 ident: &Ident,
217 generics: &Generics,
218 meta: &TypeItemAttributes,
219 all_parameters: &[FromImplParameters],
220) -> Vec<TokenStream2> {
221 let from_trait_path = util::path(["convert", "FromColorUnclamped"], meta.internal);
222 let into_trait_path = util::path(["convert", "IntoColorUnclamped"], meta.internal);
223
224 let (_, type_generics, _) = generics.split_for_impl();
225
226 let mut implementations = Vec::with_capacity(all_parameters.len());
227
228 for parameters in all_parameters {
229 let FromImplParameters {
230 color_ty,
231 generics,
232 nearest_color_ty,
233 } = parameters;
234
235 {
236 let mut generics = generics.clone();
237
238 {
239 let where_clause = generics.make_where_clause();
240 where_clause
241 .predicates
242 .push(parse_quote!(#nearest_color_ty: #from_trait_path<#color_ty>));
243 where_clause
244 .predicates
245 .push(parse_quote!(#nearest_color_ty: #into_trait_path<Self>));
246 }
247
248 let (impl_generics, _, where_clause) = generics.split_for_impl();
249
250 implementations.push(quote! {
251 #[automatically_derived]
252 impl #impl_generics #from_trait_path<#color_ty> for #ident #type_generics #where_clause {
253 fn from_color_unclamped(color: #color_ty) -> Self {
254 use #from_trait_path;
255 use #into_trait_path;
256 #nearest_color_ty::from_color_unclamped(color).into_color_unclamped()
257 }
258 }
259 });
260 }
261
262 if !meta.internal || meta.internal_not_base_type {
263 let mut generics = generics.clone();
264
265 {
266 let where_clause = generics.make_where_clause();
267 where_clause
268 .predicates
269 .push(parse_quote!(#nearest_color_ty: #from_trait_path<#ident #type_generics>));
270 where_clause
271 .predicates
272 .push(parse_quote!(#nearest_color_ty: #into_trait_path<Self>));
273 }
274
275 let (impl_generics, _, where_clause) = generics.split_for_impl();
276
277 implementations.push(quote! {
278 #[automatically_derived]
279 impl #impl_generics #from_trait_path<#ident #type_generics> for #color_ty #where_clause {
280 fn from_color_unclamped(color: #ident #type_generics) -> Self {
281 use #from_trait_path;
282 use #into_trait_path;
283 #nearest_color_ty::from_color_unclamped(color).into_color_unclamped()
284 }
285 }
286 });
287 }
288 }
289
290 implementations
291}
292
293fn generate_from_alpha_implementation(
294 ident: &Ident,
295 generics: &Generics,
296 meta: &TypeItemAttributes,
297) -> TokenStream2 {
298 let from_trait_path = util::path(["convert", "FromColorUnclamped"], meta.internal);
299 let into_trait_path = util::path(["convert", "IntoColorUnclamped"], meta.internal);
300 let alpha_path = util::path(["Alpha"], meta.internal);
301
302 let mut impl_generics = generics.clone();
303 impl_generics.params.push(parse_quote!(_C));
304 impl_generics.params.push(parse_quote!(_A));
305 {
306 let where_clause = impl_generics.make_where_clause();
307 where_clause
308 .predicates
309 .push(parse_quote!(_C: #into_trait_path<Self>));
310 }
311
312 let (_, type_generics, _) = generics.split_for_impl();
313 let (self_impl_generics, _, self_where_clause) = impl_generics.split_for_impl();
314
315 quote! {
316 #[automatically_derived]
317 impl #self_impl_generics #from_trait_path<#alpha_path<_C, _A>> for #ident #type_generics #self_where_clause {
318 fn from_color_unclamped(color: #alpha_path<_C, _A>) -> Self {
319 color.color.into_color_unclamped()
320 }
321 }
322 }
323}
324
325fn generate_from_alpha_implementation_with_internal(
326 ident: &Ident,
327 generics: &Generics,
328 meta: &TypeItemAttributes,
329 alpha_property: &IdentOrIndex,
330 alpha_type: &Type,
331) -> TokenStream2 {
332 let from_trait_path = util::path(["convert", "FromColorUnclamped"], meta.internal);
333 let into_trait_path = util::path(["convert", "IntoColorUnclamped"], meta.internal);
334 let alpha_path = util::path(["Alpha"], meta.internal);
335
336 let (_, type_generics, _) = generics.split_for_impl();
337 let mut impl_generics = generics.clone();
338 impl_generics.params.push(parse_quote!(_C));
339 {
340 let where_clause = impl_generics.make_where_clause();
341 where_clause
342 .predicates
343 .push(parse_quote!(_C: #into_trait_path<Self>));
344 }
345 let (impl_generics, _, where_clause) = impl_generics.split_for_impl();
346
347 quote! {
348 #[automatically_derived]
349 impl #impl_generics #from_trait_path<#alpha_path<_C, #alpha_type>> for #ident #type_generics #where_clause {
350 fn from_color_unclamped(color: #alpha_path<_C, #alpha_type>) -> Self {
351 use #from_trait_path;
352 use #into_trait_path;
353
354 let #alpha_path { color, alpha } = color;
355
356 let mut result: Self = color.into_color_unclamped();
357 result.#alpha_property = alpha;
358
359 result
360 }
361 }
362 }
363}