Skip to main content

gtk3_macros/
attribute_parser.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use proc_macro2::Span;
4use syn::spanned::Spanned;
5use syn::{
6    parse::{Error, Parse, ParseStream},
7    punctuated::Punctuated,
8};
9use syn::{Attribute, DeriveInput, Field, Fields, Ident, LitStr, Meta, Token, Type};
10
11mod kw {
12    syn::custom_keyword!(file);
13    syn::custom_keyword!(resource);
14    syn::custom_keyword!(string);
15
16    syn::custom_keyword!(id);
17}
18
19pub enum TemplateSource {
20    File(String),
21    Resource(String),
22    String(String),
23}
24
25impl Parse for TemplateSource {
26    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
27        let lookahead = input.lookahead1();
28        let variant = if lookahead.peek(kw::file) {
29            let _: kw::file = input.parse()?;
30            TemplateSource::File
31        } else if lookahead.peek(kw::resource) {
32            let _: kw::resource = input.parse()?;
33            TemplateSource::Resource
34        } else if lookahead.peek(kw::string) {
35            let _: kw::string = input.parse()?;
36            TemplateSource::String
37        } else {
38            return Err(lookahead.error());
39        };
40
41        let _: Token![=] = input.parse()?;
42        let lit: LitStr = input.parse()?;
43        Ok(variant(lit.value()))
44    }
45}
46
47#[derive(Debug)]
48pub enum ParseTemplateSourceError {
49    MissingAttribute,
50    Parse(syn::Error),
51}
52
53impl std::fmt::Display for ParseTemplateSourceError {
54    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55        match self {
56            Self::MissingAttribute => write!(f, "Missing 'template' attribute"),
57            Self::Parse(err) => write!(f, "{}", err),
58        }
59    }
60}
61
62impl std::error::Error for ParseTemplateSourceError {
63    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
64        match self {
65            Self::MissingAttribute => None,
66            Self::Parse(err) => Some(err),
67        }
68    }
69}
70
71pub fn parse_template_source(
72    input: &DeriveInput,
73) -> Result<TemplateSource, ParseTemplateSourceError> {
74    input
75        .attrs
76        .iter()
77        .find(|a| a.path().is_ident("template"))
78        .ok_or(ParseTemplateSourceError::MissingAttribute)?
79        .parse_args()
80        .map_err(ParseTemplateSourceError::Parse)
81}
82
83pub enum FieldAttributeArg {
84    Id(String),
85}
86
87impl Parse for FieldAttributeArg {
88    fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
89        let lookahead = input.lookahead1();
90        if lookahead.peek(kw::id) {
91            let _: kw::id = input.parse()?;
92            let _: Token![=] = input.parse()?;
93            let lit: LitStr = input.parse()?;
94            Ok(Self::Id(lit.value()))
95        } else {
96            Err(lookahead.error())
97        }
98    }
99}
100
101#[derive(Debug)]
102pub enum FieldAttributeType {
103    TemplateChild,
104}
105
106pub struct FieldAttribute {
107    pub ty: FieldAttributeType,
108    pub args: Vec<FieldAttributeArg>,
109    pub path_span: Span,
110    pub span: Span,
111}
112
113pub struct AttributedField {
114    pub ident: Ident,
115    pub ty: Type,
116    pub attr: FieldAttribute,
117}
118
119fn parse_field_attr_args(attr: &Attribute) -> Result<Vec<FieldAttributeArg>, Error> {
120    let mut field_attribute_args = Vec::new();
121    match &attr.meta {
122        Meta::List(list) => {
123            let args =
124                list.parse_args_with(Punctuated::<FieldAttributeArg, Token![,]>::parse_terminated)?;
125            for arg in args {
126                for prev_arg in &field_attribute_args {
127                    // Comparison of enum variants, not data
128                    if std::mem::discriminant(prev_arg) == std::mem::discriminant(&arg) {
129                        return Err(Error::new(
130                            attr.span(),
131                            "two instances of the same attribute \
132                            argument, each argument must be specified only once",
133                        ));
134                    }
135                }
136                field_attribute_args.push(arg);
137            }
138        }
139        Meta::Path(_) => (),
140        meta => {
141            return Err(Error::new(
142                meta.span(),
143                "invalid attribute argument type, expected `name = value` list or nothing",
144            ))
145        }
146    }
147
148    Ok(field_attribute_args)
149}
150
151fn parse_field(field: &Field) -> Result<Option<AttributedField>, Error> {
152    let field_attrs = &field.attrs;
153    let ident = match &field.ident {
154        Some(ident) => ident,
155        None => return Err(Error::new(field.span(), "expected identifier")),
156    };
157
158    let ty = &field.ty;
159    let mut attr = None;
160
161    for field_attr in field_attrs {
162        let span = field_attr.span();
163        let path_span = field_attr.path().span();
164        let ty = if field_attr.path().is_ident("template_child") {
165            Some(FieldAttributeType::TemplateChild)
166        } else {
167            None
168        };
169
170        if let Some(ty) = ty {
171            let args = parse_field_attr_args(field_attr)?;
172
173            if attr.is_none() {
174                attr = Some(FieldAttribute {
175                    ty,
176                    args,
177                    path_span,
178                    span,
179                })
180            } else {
181                return Err(Error::new(
182                    span,
183                    "multiple attributes on the same field are not supported",
184                ));
185            }
186        }
187    }
188
189    if let Some(attr) = attr {
190        Ok(Some(AttributedField {
191            ident: ident.clone(),
192            ty: ty.clone(),
193            attr,
194        }))
195    } else {
196        Ok(None)
197    }
198}
199
200pub fn parse_fields(fields: &Fields) -> Result<Vec<AttributedField>, Error> {
201    let mut attributed_fields = Vec::new();
202
203    for field in fields {
204        if !field.attrs.is_empty() {
205            if let Some(attributed_field) = parse_field(field)? {
206                attributed_fields.push(attributed_field)
207            }
208        }
209    }
210
211    Ok(attributed_fields)
212}