tinybase_derive/
utils.rs

1use proc_macro2::{Ident, TokenStream};
2use syn::{Attribute, Meta};
3
4/// This returns the attribute [`Ident`] if the attribute was found.
5pub fn has_attribute(attrs: &Vec<Attribute>, attr_name: &str) -> Option<(Ident, Meta)> {
6    for attr in attrs {
7        if let Ok(meta) = attr.parse_meta() {
8            let path = meta.path();
9            if let Some(ident) = path.get_ident() {
10                if ident == attr_name {
11                    return Some((ident.clone(), meta.clone()));
12                }
13            }
14        }
15    }
16
17    None
18}
19
20/// Get a value in an attribute.
21pub fn get_list_attr(
22    attrs: &Vec<Attribute>,
23    attr_name: &str,
24) -> Result<Vec<TokenStream>, TokenStream> {
25    let mut matches = vec![];
26
27    for attr in attrs {
28        let meta = attr.parse_meta().map_err(|err| err.to_compile_error())?;
29        if let syn::Meta::List(path) = meta {
30            if let Some(ident) = path.path.get_ident() {
31                if ident == attr_name {
32                    let tokens = attr.parse_args().map_err(|err| err.to_compile_error())?;
33                    matches.push(tokens);
34                }
35            }
36        }
37    }
38
39    Ok(matches)
40}
41
42/// Make sure the state of attributes is allowed.
43/// This returns the attribute [`Ident`] of the relevant span when validation failed.
44pub fn validate_attributes(
45    attrs: &Vec<Attribute>,
46    base: Option<&str>,
47    other: &[(&str, bool)],
48    illegal: &[&str],
49) -> Result<(), TokenStream> {
50    for attr in illegal {
51        if let Some(ident) = has_attribute(attrs, attr) {
52            return Err(
53                syn::Error::new(ident.0.span(), "This attribute is not allowed here")
54                    .to_compile_error()
55                    .into(),
56            );
57        }
58    }
59
60    for attr in other {
61        let found = has_attribute(attrs, attr.0);
62        if let Some(found) = found {
63            if let Some(base) = base {
64                if !has_attribute(attrs, base).is_some() {
65                    return Err(syn::Error::new(
66                        found.0.span(),
67                        format!("This attribute requires the #[{}] attribute", base),
68                    )
69                    .to_compile_error()
70                    .into());
71                }
72            }
73
74            match found.1 {
75                Meta::Path(_) => {
76                    if attr.1 {
77                        return Err(syn::Error::new(
78                            found.0.span(),
79                            "This attribute is missing a parameter",
80                        )
81                        .to_compile_error()
82                        .into());
83                    }
84                }
85                Meta::List(_) => {
86                    if !attr.1 {
87                        return Err(
88                            syn::Error::new(found.0.span(), "This attribute isn't a list")
89                                .to_compile_error()
90                                .into(),
91                        );
92                    }
93                }
94                _ => unimplemented!(),
95            };
96        }
97    }
98
99    Ok(())
100}