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