gtk3_macros/
attribute_parser.rs1use 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 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}