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}