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