1use 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
28pub 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 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 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 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}