1 // Take a look at the license at the top of the repository in the LICENSE file.
2
3 use anyhow::{bail, Result};
4 use proc_macro2::Span;
5 use syn::parse::Error;
6 use syn::spanned::Spanned;
7 use syn::{
8 Attribute, DeriveInput, Field, Fields, Ident, Lit, Meta, MetaList, MetaNameValue, NestedMeta,
9 Type,
10 };
11
12 pub enum TemplateSource {
13 File(String),
14 Resource(String),
15 String(String),
16 }
17
parse_template_source(input: &DeriveInput) -> Result<TemplateSource>18 pub fn parse_template_source(input: &DeriveInput) -> Result<TemplateSource> {
19 let meta = match find_attribute_meta(&input.attrs, "template")? {
20 Some(meta) => meta,
21 _ => bail!("Missing 'template' attribute"),
22 };
23
24 let meta = match meta.nested.iter().find(|n| match n {
25 NestedMeta::Meta(m) => {
26 let p = m.path();
27 p.is_ident("file") || p.is_ident("resource") || p.is_ident("string")
28 }
29 _ => false,
30 }) {
31 Some(meta) => meta,
32 _ => bail!("Invalid meta, specify one of 'file', 'resource', or 'string'"),
33 };
34
35 let (ident, v) = parse_attribute(meta)?;
36
37 match ident.as_ref() {
38 "file" => Ok(TemplateSource::File(v)),
39 "resource" => Ok(TemplateSource::Resource(v)),
40 "string" => Ok(TemplateSource::String(v)),
41 s => bail!("Unknown enum meta {}", s),
42 }
43 }
44
45 // find the #[@attr_name] attribute in @attrs
find_attribute_meta(attrs: &[Attribute], attr_name: &str) -> Result<Option<MetaList>>46 fn find_attribute_meta(attrs: &[Attribute], attr_name: &str) -> Result<Option<MetaList>> {
47 let meta = match attrs.iter().find(|a| a.path.is_ident(attr_name)) {
48 Some(a) => a.parse_meta(),
49 _ => return Ok(None),
50 };
51 match meta? {
52 Meta::List(n) => Ok(Some(n)),
53 _ => bail!("wrong meta type"),
54 }
55 }
56
57 // parse a single meta like: ident = "value"
parse_attribute(meta: &NestedMeta) -> Result<(String, String)>58 fn parse_attribute(meta: &NestedMeta) -> Result<(String, String)> {
59 let meta = match &meta {
60 NestedMeta::Meta(m) => m,
61 _ => bail!("wrong meta type"),
62 };
63 let meta = match meta {
64 Meta::NameValue(n) => n,
65 _ => bail!("wrong meta type"),
66 };
67 let value = match &meta.lit {
68 Lit::Str(s) => s.value(),
69 _ => bail!("wrong meta type"),
70 };
71
72 let ident = match meta.path.get_ident() {
73 None => bail!("missing ident"),
74 Some(ident) => ident,
75 };
76
77 Ok((ident.to_string(), value))
78 }
79
80 pub enum FieldAttributeArg {
81 Id(String),
82 }
83
84 #[derive(Debug)]
85 pub enum FieldAttributeType {
86 TemplateChild,
87 }
88
89 pub struct FieldAttribute {
90 pub ty: FieldAttributeType,
91 pub args: Vec<FieldAttributeArg>,
92 pub path_span: Span,
93 pub span: Span,
94 }
95
96 pub struct AttributedField {
97 pub ident: Ident,
98 pub ty: Type,
99 pub attr: FieldAttribute,
100 }
101
parse_field_attr_value_str(name_value: &MetaNameValue) -> Result<String, Error>102 fn parse_field_attr_value_str(name_value: &MetaNameValue) -> Result<String, Error> {
103 match &name_value.lit {
104 Lit::Str(s) => Ok(s.value()),
105 _ => Err(Error::new(
106 name_value.lit.span(),
107 "invalid value type: Expected str literal",
108 )),
109 }
110 }
111
parse_field_attr_meta( ty: &FieldAttributeType, meta: &NestedMeta, ) -> Result<FieldAttributeArg, Error>112 fn parse_field_attr_meta(
113 ty: &FieldAttributeType,
114 meta: &NestedMeta,
115 ) -> Result<FieldAttributeArg, Error> {
116 let meta = match &meta {
117 NestedMeta::Meta(m) => m,
118 _ => {
119 return Err(Error::new(
120 meta.span(),
121 "invalid type - expected a name-value pair like id = \"widget\"",
122 ))
123 }
124 };
125 let name_value = match meta {
126 Meta::NameValue(n) => n,
127 _ => {
128 return Err(Error::new(
129 meta.span(),
130 "invalid type - expected a name-value pair like id = \"widget\"",
131 ))
132 }
133 };
134 let ident = match name_value.path.get_ident() {
135 None => {
136 return Err(Error::new(
137 name_value.path.span(),
138 "invalid name type - expected identifier",
139 ))
140 }
141 Some(ident) => ident,
142 };
143
144 let ident_str = ident.to_string();
145 let unknown_err = Err(Error::new(
146 ident.span(),
147 &format!("unknown attribute argument: `{}`", ident_str),
148 ));
149 let value = match ty {
150 FieldAttributeType::TemplateChild => match ident_str.as_str() {
151 "id" => FieldAttributeArg::Id(parse_field_attr_value_str(name_value)?),
152 _ => return unknown_err,
153 },
154 };
155
156 Ok(value)
157 }
158
parse_field_attr_args( ty: &FieldAttributeType, attr: &Attribute, ) -> Result<Vec<FieldAttributeArg>, Error>159 fn parse_field_attr_args(
160 ty: &FieldAttributeType,
161 attr: &Attribute,
162 ) -> Result<Vec<FieldAttributeArg>, Error> {
163 let mut field_attribute_args = Vec::new();
164 match attr.parse_meta()? {
165 Meta::List(list) => {
166 for meta in &list.nested {
167 let new_arg = parse_field_attr_meta(ty, meta)?;
168 for arg in &field_attribute_args {
169 // Comparison of enum variants, not data
170 if std::mem::discriminant(arg) == std::mem::discriminant(&new_arg) {
171 return Err(Error::new(
172 meta.span(),
173 "two instances of the same attribute \
174 argument, each argument must be specified only once",
175 ));
176 }
177 }
178 field_attribute_args.push(new_arg);
179 }
180 }
181 Meta::Path(_) => (),
182 meta => {
183 return Err(Error::new(
184 meta.span(),
185 "invalid attribute argument type, expected `name = value` list or nothing",
186 ))
187 }
188 }
189
190 Ok(field_attribute_args)
191 }
192
parse_field(field: &Field) -> Result<Option<AttributedField>, Error>193 fn parse_field(field: &Field) -> Result<Option<AttributedField>, Error> {
194 let field_attrs = &field.attrs;
195 let ident = match &field.ident {
196 Some(ident) => ident,
197 None => return Err(Error::new(field.span(), "expected identifier")),
198 };
199
200 let ty = &field.ty;
201 let mut attr = None;
202
203 for field_attr in field_attrs {
204 let span = field_attr.span();
205 let path_span = field_attr.path.span();
206 let ty = if field_attr.path.is_ident("template_child") {
207 Some(FieldAttributeType::TemplateChild)
208 } else {
209 None
210 };
211
212 if let Some(ty) = ty {
213 let args = parse_field_attr_args(&ty, field_attr)?;
214
215 if attr.is_none() {
216 attr = Some(FieldAttribute {
217 ty,
218 args,
219 path_span,
220 span,
221 })
222 } else {
223 return Err(Error::new(
224 span,
225 "multiple attributes on the same field are not supported",
226 ));
227 }
228 }
229 }
230
231 if let Some(attr) = attr {
232 Ok(Some(AttributedField {
233 ident: ident.clone(),
234 ty: ty.clone(),
235 attr,
236 }))
237 } else {
238 Ok(None)
239 }
240 }
241
parse_fields(fields: &Fields) -> Result<Vec<AttributedField>, Error>242 pub fn parse_fields(fields: &Fields) -> Result<Vec<AttributedField>, Error> {
243 let mut attributed_fields = Vec::new();
244
245 for field in fields {
246 if !field.attrs.is_empty() {
247 if let Some(attributed_field) = parse_field(field)? {
248 attributed_fields.push(attributed_field)
249 }
250 }
251 }
252
253 Ok(attributed_fields)
254 }
255