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}