palette_derive/meta/
type_item_attributes.rs

1use std::collections::HashSet;
2
3use by_address::ByAddress;
4use quote::quote;
5use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, Expr, ExprLit};
6use syn::{Ident, Lit, Meta, MetaNameValue, Type};
7
8use crate::color_types::{ColorGroup, COLOR_GROUPS};
9
10use super::AttributeArgumentParser;
11
12#[derive(Default)]
13pub struct TypeItemAttributes {
14    pub skip_derives: HashSet<String>,
15    pub internal: bool,
16    pub internal_not_base_type: bool,
17    pub component: Option<Type>,
18    pub white_point: Option<Type>,
19    pub rgb_standard: Option<Type>,
20    pub luma_standard: Option<Type>,
21    pub(crate) color_groups: HashSet<ByAddress<&'static ColorGroup>>,
22}
23
24impl AttributeArgumentParser for TypeItemAttributes {
25    fn argument(&mut self, argument: Meta) -> Result<(), Vec<syn::Error>> {
26        let argument_name = argument.path().get_ident().map(ToString::to_string);
27
28        match argument_name.as_deref() {
29            Some("skip_derives") => {
30                if let Meta::List(list) = argument {
31                    let skipped = list
32                        .parse_args_with(Punctuated::<Ident, Comma>::parse_terminated)
33                        .map_err(|error| vec![error])?;
34
35                    let mut errors = Vec::new();
36                    for skipped_color in skipped {
37                        let color_name = skipped_color.to_string();
38                        self.skip_derives.insert(color_name.clone());
39
40                        let color_group = COLOR_GROUPS
41                            .iter()
42                            .find(|group| group.check_availability(&color_name).is_ok());
43
44                        let group = if let Some(&group) = color_group {
45                            group
46                        } else {
47                            errors.push(syn::Error::new(
48                                skipped_color.span(),
49                                format!("`{}` is not a valid color type", skipped_color),
50                            ));
51                            continue;
52                        };
53
54                        let infer_group = group
55                            .find_type_by_name(&color_name)
56                            .map_or(true, |ty| ty.infer_group);
57
58                        if infer_group {
59                            self.color_groups.insert(group.into());
60                        }
61                    }
62
63                    if !errors.is_empty() {
64                        return Err(errors);
65                    }
66                } else {
67                    return Err(vec![syn::Error::new(
68                        argument.span(),
69                        "expected `skip_derives` to have a list of color type names, like `skip_derives(Xyz, Luma, Rgb)`",
70                    )]);
71                }
72            }
73            Some("component") => {
74                get_meta_type_argument(argument, &mut self.component)?;
75            }
76            Some("white_point") => {
77                get_meta_type_argument(argument, &mut self.white_point)?;
78            }
79            Some("rgb_standard") => {
80                get_meta_type_argument(argument, &mut self.rgb_standard)?;
81            }
82            Some("luma_standard") => {
83                get_meta_type_argument(argument, &mut self.luma_standard)?;
84            }
85            Some("palette_internal") => {
86                if let Meta::Path(_) = argument {
87                    self.internal = true;
88                } else {
89                    return Err(vec![syn::Error::new(
90                        argument.span(),
91                        "expected `palette_internal` to a literal without value",
92                    )]);
93                }
94            }
95            Some("palette_internal_not_base_type") => {
96                if let Meta::Path(_) = argument {
97                    self.internal_not_base_type = true;
98                } else {
99                    return Err(vec![syn::Error::new(
100                        argument.span(),
101                        "expected `palette_internal` to a literal without value",
102                    )]);
103                }
104            }
105            _ => {
106                return Err(vec![syn::Error::new(
107                    argument.span(),
108                    format!("`{}` is not a known type item attribute", quote!(#argument)),
109                )]);
110            }
111        }
112
113        Ok(())
114    }
115}
116
117fn get_meta_type_argument(
118    argument: Meta,
119    attribute: &mut Option<Type>,
120) -> Result<(), Vec<syn::Error>> {
121    if attribute.is_none() {
122        let result = if let Meta::NameValue(MetaNameValue {
123            value: Expr::Lit(ExprLit {
124                lit: Lit::Str(ty), ..
125            }),
126            ..
127        }) = argument
128        {
129            *attribute = Some(ty.parse().map_err(|error| vec![error])?);
130            Ok(())
131        } else {
132            Err((argument.span(), argument.path()))
133        };
134
135        if let Err((span, path)) = result {
136            let name = path.get_ident().unwrap();
137            let message = format!("expected `{name}` to be a type or type parameter in a string, like `{name} = \"T\"`");
138            Err(vec![syn::Error::new(span, message)])
139        } else {
140            Ok(())
141        }
142    } else {
143        let name = argument.path().get_ident().unwrap();
144        Err(vec![syn::Error::new(
145            argument.span(),
146            format!("`{name}` appears more than once"),
147        )])
148    }
149}