palette_derive/alpha/
with_alpha.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};

use quote::quote;
use syn::{parse_quote, DeriveInput, Generics, Ident, Type};

use crate::{
    meta::{
        parse_field_attributes, parse_namespaced_attributes, FieldAttributes, IdentOrIndex,
        TypeItemAttributes,
    },
    util,
};

pub fn derive(item: TokenStream) -> ::std::result::Result<TokenStream, Vec<::syn::parse::Error>> {
    let DeriveInput {
        ident,
        generics: original_generics,
        data,
        attrs,
        ..
    } = syn::parse(item).map_err(|error| vec![error])?;
    let generics = original_generics;

    let (item_meta, item_errors) = parse_namespaced_attributes::<TypeItemAttributes>(attrs);

    let (fields_meta, field_errors) = if let syn::Data::Struct(struct_data) = data {
        parse_field_attributes::<FieldAttributes>(struct_data.fields)
    } else {
        return Err(vec![syn::Error::new(
            Span::call_site(),
            "only structs are supported",
        )]);
    };

    let implementation = if let Some((alpha_property, alpha_type)) = fields_meta.alpha_property {
        implement_for_internal_alpha(&ident, &generics, &alpha_property, &alpha_type, &item_meta)
    } else {
        implement_for_external_alpha(&ident, &generics, &item_meta)
    };

    let item_errors = item_errors
        .into_iter()
        .map(|error| error.into_compile_error());
    let field_errors = field_errors
        .into_iter()
        .map(|error| error.into_compile_error());

    Ok(quote! {
        #(#item_errors)*
        #(#field_errors)*

        #implementation
    }
    .into())
}

fn implement_for_internal_alpha(
    ident: &Ident,
    generics: &Generics,
    alpha_property: &IdentOrIndex,
    alpha_type: &Type,
    item_meta: &TypeItemAttributes,
) -> TokenStream2 {
    let with_alpha_trait_path = util::path(["WithAlpha"], item_meta.internal);
    let stimulus_trait_path = util::path(["stimulus", "Stimulus"], item_meta.internal);

    let (impl_generics, type_generics, where_clause) = generics.split_for_impl();

    quote! {
        #[automatically_derived]
        impl #impl_generics #with_alpha_trait_path<#alpha_type> for #ident #type_generics #where_clause {
            type Color = Self;
            type WithAlpha = Self;

            fn with_alpha(mut self, alpha: #alpha_type) -> Self::WithAlpha {
                self.#alpha_property = alpha;
                self
            }

            fn without_alpha(mut self) -> Self::Color {
                self.#alpha_property = #stimulus_trait_path::max_intensity();
                self
            }

            fn split(mut self) -> (Self::Color, #alpha_type) {
                let opaque_alpha = #stimulus_trait_path::max_intensity();
                let alpha = core::mem::replace(&mut self.#alpha_property, opaque_alpha);
                (self, alpha)
            }
        }
    }
}

fn implement_for_external_alpha(
    ident: &Ident,
    generics: &Generics,
    item_meta: &TypeItemAttributes,
) -> TokenStream2 {
    let with_alpha_trait_path = util::path(["WithAlpha"], item_meta.internal);
    let stimulus_trait_path = util::path(["stimulus", "Stimulus"], item_meta.internal);
    let alpha_path = util::path(["Alpha"], item_meta.internal);

    let (_, type_generics, _) = generics.split_for_impl();

    let alpha_type: Type = parse_quote!(_A);
    let mut impl_generics = generics.clone();
    impl_generics.params.push(parse_quote!(_A));
    impl_generics
        .make_where_clause()
        .predicates
        .push(parse_quote!(_A: #stimulus_trait_path));
    let (impl_generics, _, where_clause) = impl_generics.split_for_impl();

    quote! {
        #[automatically_derived]
        impl #impl_generics #with_alpha_trait_path<#alpha_type> for #ident #type_generics #where_clause {
            type Color = Self;
            type WithAlpha = #alpha_path<Self, #alpha_type>;

            fn with_alpha(self, alpha: #alpha_type) -> Self::WithAlpha {
                #alpha_path {
                    color: self,
                    alpha
                }
            }

            fn without_alpha(self) -> Self::Color {
                self
            }

            fn split(self) -> (Self::Color, #alpha_type) {
                (self, #stimulus_trait_path::max_intensity())
            }
        }
    }
}