palette_derive/meta/
mod.rs
1use proc_macro2::TokenStream;
2use syn::punctuated::Punctuated;
3use syn::spanned::Spanned;
4use syn::{
5 parse::{Parse, ParseStream, Parser, Result},
6 token::Comma,
7};
8use syn::{Attribute, Fields, Ident, Index, LitStr, Meta, Token, Type};
9
10pub use self::field_attributes::*;
11pub use self::type_item_attributes::*;
12
13mod field_attributes;
14mod type_item_attributes;
15
16pub fn parse_namespaced_attributes<T: AttributeArgumentParser>(
17 attributes: Vec<Attribute>,
18) -> (T, Vec<::syn::parse::Error>) {
19 let mut result = T::default();
20 let mut errors = Vec::new();
21
22 for attribute in attributes {
23 let is_palette_attribute = attribute
24 .meta
25 .path()
26 .get_ident()
27 .map(|name| name == "palette")
28 .unwrap_or(false);
29
30 if !is_palette_attribute {
31 continue;
32 }
33
34 let meta_list = match attribute.meta.require_list() {
35 Ok(list) => list,
36 Err(error) => {
37 errors.push(error);
38 continue;
39 }
40 };
41
42 if meta_list.tokens.is_empty() {
43 errors.push(::syn::parse::Error::new(
44 attribute.path().span(),
45 "expected `palette(...)`",
46 ));
47
48 continue;
49 }
50
51 let parse_result =
52 Punctuated::<_, Comma>::parse_terminated.parse2(meta_list.tokens.clone());
53 match parse_result {
54 Ok(meta) => {
55 for argument in meta {
56 if let Err(new_error) = result.argument(argument) {
57 errors.extend(new_error);
58 }
59 }
60 }
61 Err(error) => errors.push(error),
62 }
63 }
64
65 (result, errors)
66}
67
68pub fn parse_field_attributes<T: FieldAttributeArgumentParser>(
69 fields: Fields,
70) -> (T, Vec<::syn::parse::Error>) {
71 let mut result = T::default();
72 let mut errors = Vec::new();
73
74 let attributes = fields.into_iter().enumerate().flat_map(|(index, field)| {
75 let field_name = field
76 .ident
77 .map(IdentOrIndex::Ident)
78 .unwrap_or_else(|| IdentOrIndex::Index(index.into()));
79 let ty = field.ty;
80
81 field
82 .attrs
83 .into_iter()
84 .map(move |attribute| (field_name.clone(), ty.clone(), attribute))
85 });
86
87 for (field_name, ty, attribute) in attributes {
88 let is_palette_attribute = attribute
89 .path()
90 .get_ident()
91 .map(|name| name == "palette")
92 .unwrap_or(false);
93
94 if !is_palette_attribute {
95 continue;
96 }
97
98 let meta_list = match attribute.meta.require_list() {
99 Ok(list) => list,
100 Err(error) => {
101 errors.push(error);
102 continue;
103 }
104 };
105
106 if meta_list.tokens.is_empty() {
107 errors.push(::syn::parse::Error::new(
108 attribute.path().span(),
109 "expected `palette(...)`",
110 ));
111
112 continue;
113 }
114
115 let parse_result =
116 Punctuated::<_, Comma>::parse_terminated.parse2(meta_list.tokens.clone());
117 match parse_result {
118 Ok(meta) => {
119 for argument in meta {
120 if let Err(new_errors) = result.argument(&field_name, &ty, argument) {
121 errors.extend(new_errors);
122 }
123 }
124 }
125 Err(error) => errors.push(error),
126 }
127 }
128
129 (result, errors)
130}
131
132pub fn assert_path_meta(meta: &Meta) -> Result<()> {
133 if !matches!(meta, Meta::Path(_)) {
134 return Err(::syn::parse::Error::new(
135 meta.span(),
136 "expected the attribute to be just an identifier or a path",
137 ));
138 }
139
140 Ok(())
141}
142
143#[derive(PartialEq)]
144pub struct KeyValuePair {
145 pub key: Ident,
146 pub value: Ident,
147}
148
149impl Parse for KeyValuePair {
150 fn parse(input: ParseStream) -> Result<Self> {
151 let key: Ident = input.parse()?;
152 input.parse::<Token![=]>()?;
153 let value = input.parse::<LitStr>()?.parse::<Ident>()?;
154 Ok(KeyValuePair { key, value })
155 }
156}
157
158impl PartialEq<str> for KeyValuePair {
159 fn eq(&self, other: &str) -> bool {
160 self.key == other
161 }
162}
163
164#[derive(Clone)]
165pub enum IdentOrIndex {
166 Index(Index),
167 Ident(Ident),
168}
169
170impl PartialEq for IdentOrIndex {
171 fn eq(&self, other: &IdentOrIndex) -> bool {
172 match (self, other) {
173 (IdentOrIndex::Index(this), IdentOrIndex::Index(other)) => this.index == other.index,
174 (IdentOrIndex::Ident(this), IdentOrIndex::Ident(other)) => this == other,
175 _ => false,
176 }
177 }
178}
179
180impl Eq for IdentOrIndex {}
181
182impl ::std::hash::Hash for IdentOrIndex {
183 fn hash<H: ::std::hash::Hasher>(&self, hasher: &mut H) {
184 ::std::mem::discriminant(self).hash(hasher);
185
186 match *self {
187 IdentOrIndex::Index(ref index) => index.index.hash(hasher),
188 IdentOrIndex::Ident(ref ident) => ident.hash(hasher),
189 }
190 }
191}
192
193impl ::quote::ToTokens for IdentOrIndex {
194 fn to_tokens(&self, tokens: &mut TokenStream) {
195 match *self {
196 IdentOrIndex::Index(ref index) => index.to_tokens(tokens),
197 IdentOrIndex::Ident(ref ident) => ident.to_tokens(tokens),
198 }
199 }
200}
201
202pub trait AttributeArgumentParser: Default {
203 fn argument(&mut self, argument: Meta) -> std::result::Result<(), Vec<syn::Error>>;
204}
205
206pub trait FieldAttributeArgumentParser: Default {
207 fn argument(
208 &mut self,
209 field_name: &IdentOrIndex,
210 ty: &Type,
211 argument: Meta,
212 ) -> std::result::Result<(), Vec<syn::Error>>;
213}