palette_derive/alpha/
with_alpha.rs
1use proc_macro::TokenStream;
2use proc_macro2::{Span, TokenStream as TokenStream2};
3
4use quote::quote;
5use syn::{parse_quote, DeriveInput, Generics, Ident, Type};
6
7use crate::{
8 meta::{
9 parse_field_attributes, parse_namespaced_attributes, FieldAttributes, IdentOrIndex,
10 TypeItemAttributes,
11 },
12 util,
13};
14
15pub fn derive(item: TokenStream) -> ::std::result::Result<TokenStream, Vec<::syn::parse::Error>> {
16 let DeriveInput {
17 ident,
18 generics: original_generics,
19 data,
20 attrs,
21 ..
22 } = syn::parse(item).map_err(|error| vec![error])?;
23 let generics = original_generics;
24
25 let (item_meta, item_errors) = parse_namespaced_attributes::<TypeItemAttributes>(attrs);
26
27 let (fields_meta, field_errors) = if let syn::Data::Struct(struct_data) = data {
28 parse_field_attributes::<FieldAttributes>(struct_data.fields)
29 } else {
30 return Err(vec![syn::Error::new(
31 Span::call_site(),
32 "only structs are supported",
33 )]);
34 };
35
36 let implementation = if let Some((alpha_property, alpha_type)) = fields_meta.alpha_property {
37 implement_for_internal_alpha(&ident, &generics, &alpha_property, &alpha_type, &item_meta)
38 } else {
39 implement_for_external_alpha(&ident, &generics, &item_meta)
40 };
41
42 let item_errors = item_errors
43 .into_iter()
44 .map(|error| error.into_compile_error());
45 let field_errors = field_errors
46 .into_iter()
47 .map(|error| error.into_compile_error());
48
49 Ok(quote! {
50 #(#item_errors)*
51 #(#field_errors)*
52
53 #implementation
54 }
55 .into())
56}
57
58fn implement_for_internal_alpha(
59 ident: &Ident,
60 generics: &Generics,
61 alpha_property: &IdentOrIndex,
62 alpha_type: &Type,
63 item_meta: &TypeItemAttributes,
64) -> TokenStream2 {
65 let with_alpha_trait_path = util::path(["WithAlpha"], item_meta.internal);
66 let stimulus_trait_path = util::path(["stimulus", "Stimulus"], item_meta.internal);
67
68 let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
69
70 quote! {
71 #[automatically_derived]
72 impl #impl_generics #with_alpha_trait_path<#alpha_type> for #ident #type_generics #where_clause {
73 type Color = Self;
74 type WithAlpha = Self;
75
76 fn with_alpha(mut self, alpha: #alpha_type) -> Self::WithAlpha {
77 self.#alpha_property = alpha;
78 self
79 }
80
81 fn without_alpha(mut self) -> Self::Color {
82 self.#alpha_property = #stimulus_trait_path::max_intensity();
83 self
84 }
85
86 fn split(mut self) -> (Self::Color, #alpha_type) {
87 let opaque_alpha = #stimulus_trait_path::max_intensity();
88 let alpha = core::mem::replace(&mut self.#alpha_property, opaque_alpha);
89 (self, alpha)
90 }
91 }
92 }
93}
94
95fn implement_for_external_alpha(
96 ident: &Ident,
97 generics: &Generics,
98 item_meta: &TypeItemAttributes,
99) -> TokenStream2 {
100 let with_alpha_trait_path = util::path(["WithAlpha"], item_meta.internal);
101 let stimulus_trait_path = util::path(["stimulus", "Stimulus"], item_meta.internal);
102 let alpha_path = util::path(["Alpha"], item_meta.internal);
103
104 let (_, type_generics, _) = generics.split_for_impl();
105
106 let alpha_type: Type = parse_quote!(_A);
107 let mut impl_generics = generics.clone();
108 impl_generics.params.push(parse_quote!(_A));
109 impl_generics
110 .make_where_clause()
111 .predicates
112 .push(parse_quote!(_A: #stimulus_trait_path));
113 let (impl_generics, _, where_clause) = impl_generics.split_for_impl();
114
115 quote! {
116 #[automatically_derived]
117 impl #impl_generics #with_alpha_trait_path<#alpha_type> for #ident #type_generics #where_clause {
118 type Color = Self;
119 type WithAlpha = #alpha_path<Self, #alpha_type>;
120
121 fn with_alpha(self, alpha: #alpha_type) -> Self::WithAlpha {
122 #alpha_path {
123 color: self,
124 alpha
125 }
126 }
127
128 fn without_alpha(self) -> Self::Color {
129 self
130 }
131
132 fn split(self) -> (Self::Color, #alpha_type) {
133 (self, #stimulus_trait_path::max_intensity())
134 }
135 }
136 }
137}