vizia_derive/
attr.rs

1// Adapted from Druid attr.rs
2
3// Copyright 2019 The Druid Authors.
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9//     http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17use proc_macro2::{Ident, Literal, Span, TokenStream, TokenTree};
18use quote::{quote, quote_spanned};
19use syn::{parenthesized, Error, ExprPath, Lit, LitStr};
20
21const BASE_DATA_ATTR_PATH: &str = "data";
22const BASE_LENS_ATTR_PATH: &str = "lens";
23const IGNORE_ATTR_PATH: &str = "ignore";
24const DATA_SAME_FN_ATTR_PATH: &str = "same_fn";
25const DATA_EQ_ATTR_PATH: &str = "eq";
26const LENS_NAME_OVERRIDE_ATTR_PATH: &str = "name";
27
28/// The fields for a struct or an enum variant.
29pub struct Fields<Attrs> {
30    pub kind: FieldKind,
31    fields: Vec<Field<Attrs>>,
32}
33
34#[derive(Debug, Clone, PartialEq, Eq)]
35pub enum FieldKind {
36    Named,
37    // this also covers Unit; we determine 'unit-ness' based on the number
38    // of fields.
39    Unnamed,
40}
41
42#[derive(Debug)]
43pub enum FieldIdent {
44    Named(String),
45    Unnamed(usize),
46}
47
48impl FieldIdent {
49    pub fn unwrap_named(&self) -> syn::Ident {
50        if let FieldIdent::Named(s) = self {
51            syn::Ident::new(s, Span::call_site())
52        } else {
53            panic!("Unwrap named called on unnamed FieldIdent");
54        }
55    }
56}
57
58pub struct Field<Attrs> {
59    pub ident: FieldIdent,
60    pub ty: syn::Type,
61    pub vis: syn::Visibility,
62    pub attrs: Attrs,
63}
64
65pub enum DataAttr {
66    Empty,
67    Ignore,
68    SameFn(ExprPath),
69    Eq,
70}
71
72impl PartialEq for DataAttr {
73    fn eq(&self, other: &Self) -> bool {
74        matches!(
75            (self, other),
76            (DataAttr::Empty, DataAttr::Empty)
77                | (DataAttr::Ignore, DataAttr::Ignore)
78                | (DataAttr::SameFn(_), DataAttr::SameFn(_))
79                | (DataAttr::Eq, DataAttr::Eq)
80        )
81    }
82}
83
84#[derive(Debug)]
85pub struct LensAttrs {
86    /// `true` if this field should be ignored.
87    pub ignore: bool,
88    pub lens_name_override: Option<Ident>,
89}
90
91impl Fields<DataAttr> {
92    pub fn parse_ast(fields: &syn::Fields) -> Result<Self, Error> {
93        let kind = match fields {
94            syn::Fields::Named(_) => FieldKind::Named,
95            syn::Fields::Unnamed(_) | syn::Fields::Unit => FieldKind::Unnamed,
96        };
97
98        let fields = fields
99            .iter()
100            .enumerate()
101            .map(|(i, field)| Field::<DataAttr>::parse_ast(field, i))
102            .collect::<Result<Vec<_>, _>>()?;
103
104        Ok(Fields { kind, fields })
105    }
106}
107
108impl Fields<LensAttrs> {
109    pub fn parse_ast(fields: &syn::Fields) -> Result<Self, Error> {
110        let kind = match fields {
111            syn::Fields::Named(_) => FieldKind::Named,
112            syn::Fields::Unnamed(_) | syn::Fields::Unit => FieldKind::Unnamed,
113        };
114
115        let fields = fields
116            .iter()
117            .enumerate()
118            .map(|(i, field)| Field::<LensAttrs>::parse_ast(field, i))
119            .collect::<Result<Vec<_>, _>>()?;
120
121        Ok(Fields { kind, fields })
122    }
123}
124
125impl<Attrs> Fields<Attrs> {
126    pub fn len(&self) -> usize {
127        self.fields.len()
128    }
129
130    pub fn iter(&self) -> impl Iterator<Item = &Field<Attrs>> {
131        self.fields.iter()
132    }
133}
134
135impl Field<DataAttr> {
136    pub fn parse_ast(field: &syn::Field, index: usize) -> Result<Self, Error> {
137        let ident = match field.ident.as_ref() {
138            Some(ident) => FieldIdent::Named(ident.to_string().trim_start_matches("r#").to_owned()),
139            None => FieldIdent::Unnamed(index),
140        };
141
142        let ty = field.ty.clone();
143
144        let vis = field.vis.clone();
145
146        let mut data_attr = DataAttr::Empty;
147        for attr in field.attrs.iter() {
148            if attr.path().is_ident(BASE_DATA_ATTR_PATH) {
149                attr.parse_nested_meta(|meta| {
150                    if meta.path.is_ident(IGNORE_ATTR_PATH) {
151                        data_attr = DataAttr::Ignore;
152                        return Ok(());
153                    }
154
155                    if meta.path.is_ident(DATA_EQ_ATTR_PATH) {
156                        data_attr = DataAttr::Eq;
157                        return Ok(());
158                    }
159
160                    if meta.path.is_ident(DATA_SAME_FN_ATTR_PATH) {
161                        let content;
162                        parenthesized!(content in meta.input);
163                        let lit: LitStr = content.parse()?;
164                        let expr = parse_lit_into_expr_path(&Lit::Str(lit))?;
165                        data_attr = DataAttr::SameFn(expr);
166                        return Ok(());
167                    }
168
169                    Err(Error::new(
170                        meta.input.span(),
171                        "Expected attribute list of the form #[data(one, two)]",
172                    ))
173                })?;
174            }
175        }
176        Ok(Field { ident, ty, vis, attrs: data_attr })
177    }
178
179    pub fn same_fn_path_tokens(&self) -> TokenStream {
180        match &self.attrs {
181            DataAttr::SameFn(f) => quote!(#f),
182            DataAttr::Eq => quote!(::core::cmp::PartialEq::eq),
183            // this should not be called for DataAttr::Ignore
184            DataAttr::Ignore => quote!(compiler_error!),
185            DataAttr::Empty => {
186                let span = Span::call_site();
187                quote_spanned!(span=> Data::same)
188            }
189        }
190    }
191}
192
193impl Field<LensAttrs> {
194    pub fn parse_ast(field: &syn::Field, index: usize) -> Result<Self, Error> {
195        let ident = match field.ident.as_ref() {
196            Some(ident) => FieldIdent::Named(ident.to_string().trim_start_matches("r#").to_owned()),
197            None => FieldIdent::Unnamed(index),
198        };
199
200        let ty = field.ty.clone();
201
202        let vis = field.vis.clone();
203
204        let mut ignore = false;
205        let mut lens_name_override = None;
206
207        for attr in field.attrs.iter() {
208            if attr.path().is_ident(BASE_LENS_ATTR_PATH) {
209                attr.parse_nested_meta(|meta| {
210                    if meta.path.is_ident(IGNORE_ATTR_PATH) {
211                        if ignore {
212                            return Err(Error::new(meta.input.span(), "Duplicate attribute"));
213                        }
214
215                        ignore = true;
216                        return Ok(());
217                    }
218
219                    if meta.path.is_ident(LENS_NAME_OVERRIDE_ATTR_PATH) {
220                        if lens_name_override.is_some() {
221                            return Err(Error::new(meta.input.span(), "Duplicate attribute"));
222                        }
223
224                        let content;
225                        parenthesized!(content in meta.input);
226                        let lit: LitStr = content.parse()?;
227                        let ident = parse_lit_into_ident(&Lit::Str(lit))?;
228                        lens_name_override = Some(ident);
229                        return Ok(());
230                    }
231
232                    Err(Error::new(
233                        meta.input.span(),
234                        "Expected attribute list of the form #[lens(one, two)]",
235                    ))
236                })?;
237            }
238        }
239        Ok(Field { ident, ty, vis, attrs: LensAttrs { ignore, lens_name_override } })
240    }
241}
242
243impl<Attrs> Field<Attrs> {
244    pub fn ident_tokens(&self) -> TokenTree {
245        match self.ident {
246            FieldIdent::Named(ref s) => Ident::new(s, Span::call_site()).into(),
247            FieldIdent::Unnamed(num) => Literal::usize_unsuffixed(num).into(),
248        }
249    }
250
251    pub fn ident_string(&self) -> String {
252        match self.ident {
253            FieldIdent::Named(ref s) => s.clone(),
254            FieldIdent::Unnamed(num) => num.to_string(),
255        }
256    }
257}
258
259fn parse_lit_into_expr_path(lit: &syn::Lit) -> Result<ExprPath, Error> {
260    let string = if let syn::Lit::Str(lit) = lit {
261        lit
262    } else {
263        return Err(Error::new(lit.span(), "expected str, found... something else"));
264    };
265
266    let tokens = syn::parse_str(&string.value())?;
267    syn::parse2(tokens)
268}
269
270fn parse_lit_into_ident(lit: &syn::Lit) -> Result<Ident, Error> {
271    let ident = if let syn::Lit::Str(lit) = lit {
272        Ident::new(&lit.value(), lit.span())
273    } else {
274        return Err(Error::new(lit.span(), "expected str, found... something else"));
275    };
276
277    Ok(ident)
278}