1 | use std::{slice, vec}; |
2 | |
3 | use proc_macro2::{Span, TokenStream}; |
4 | use quote::{quote, quote_spanned, ToTokens}; |
5 | use syn::ext::IdentExt; |
6 | use syn::parse::Parser; |
7 | use syn::spanned::Spanned; |
8 | use syn::Token; |
9 | |
10 | use crate::usage::{ |
11 | self, IdentRefSet, IdentSet, LifetimeRefSet, LifetimeSet, UsesLifetimes, UsesTypeParams, |
12 | }; |
13 | use crate::{Error, FromField, FromVariant, Result}; |
14 | |
15 | /// A struct or enum body. |
16 | /// |
17 | /// `V` is the type which receives any encountered variants, and `F` receives struct fields. |
18 | #[derive (Debug, Clone, PartialEq, Eq)] |
19 | pub enum Data<V, F> { |
20 | Enum(Vec<V>), |
21 | Struct(Fields<F>), |
22 | } |
23 | |
24 | impl<V, F> Data<V, F> { |
25 | /// Creates an empty body of the same shape as the passed-in body. |
26 | /// |
27 | /// # Panics |
28 | /// This function will panic if passed `syn::Data::Union`. |
29 | pub fn empty_from(src: &syn::Data) -> Self { |
30 | match *src { |
31 | syn::Data::Enum(_) => Data::Enum(vec![]), |
32 | syn::Data::Struct(ref vd) => Data::Struct(Fields::empty_from(&vd.fields)), |
33 | syn::Data::Union(_) => panic!("Unions are not supported" ), |
34 | } |
35 | } |
36 | |
37 | /// Creates an empty body of the same shape as the passed-in body. |
38 | /// |
39 | /// `darling` does not support unions; calling this function with a union body will return an error. |
40 | pub fn try_empty_from(src: &syn::Data) -> Result<Self> { |
41 | match *src { |
42 | syn::Data::Enum(_) => Ok(Data::Enum(vec![])), |
43 | syn::Data::Struct(ref vd) => Ok(Data::Struct(Fields::empty_from(&vd.fields))), |
44 | // This deliberately doesn't set a span on the error message, as the error is most useful if |
45 | // applied to the call site of the offending macro. Given that the message is very generic, |
46 | // putting it on the union keyword ends up being confusing. |
47 | syn::Data::Union(_) => Err(Error::custom("Unions are not supported" )), |
48 | } |
49 | } |
50 | |
51 | /// Creates a new `Data<&'a V, &'a F>` instance from `Data<V, F>`. |
52 | pub fn as_ref(&self) -> Data<&V, &F> { |
53 | match *self { |
54 | Data::Enum(ref variants) => Data::Enum(variants.iter().collect()), |
55 | Data::Struct(ref data) => Data::Struct(data.as_ref()), |
56 | } |
57 | } |
58 | |
59 | /// Applies a function `V -> U` on enum variants, if this is an enum. |
60 | pub fn map_enum_variants<T, U>(self, map: T) -> Data<U, F> |
61 | where |
62 | T: FnMut(V) -> U, |
63 | { |
64 | match self { |
65 | Data::Enum(v) => Data::Enum(v.into_iter().map(map).collect()), |
66 | Data::Struct(f) => Data::Struct(f), |
67 | } |
68 | } |
69 | |
70 | /// Applies a function `F -> U` on struct fields, if this is a struct. |
71 | pub fn map_struct_fields<T, U>(self, map: T) -> Data<V, U> |
72 | where |
73 | T: FnMut(F) -> U, |
74 | { |
75 | match self { |
76 | Data::Enum(v) => Data::Enum(v), |
77 | Data::Struct(f) => Data::Struct(f.map(map)), |
78 | } |
79 | } |
80 | |
81 | /// Applies a function to the `Fields` if this is a struct. |
82 | pub fn map_struct<T, U>(self, mut map: T) -> Data<V, U> |
83 | where |
84 | T: FnMut(Fields<F>) -> Fields<U>, |
85 | { |
86 | match self { |
87 | Data::Enum(v) => Data::Enum(v), |
88 | Data::Struct(f) => Data::Struct(map(f)), |
89 | } |
90 | } |
91 | |
92 | /// Consumes the `Data`, returning `Fields<F>` if it was a struct. |
93 | pub fn take_struct(self) -> Option<Fields<F>> { |
94 | match self { |
95 | Data::Enum(_) => None, |
96 | Data::Struct(f) => Some(f), |
97 | } |
98 | } |
99 | |
100 | /// Consumes the `Data`, returning `Vec<V>` if it was an enum. |
101 | pub fn take_enum(self) -> Option<Vec<V>> { |
102 | match self { |
103 | Data::Enum(v) => Some(v), |
104 | Data::Struct(_) => None, |
105 | } |
106 | } |
107 | |
108 | /// Returns `true` if this instance is `Data::Enum`. |
109 | pub fn is_enum(&self) -> bool { |
110 | match *self { |
111 | Data::Enum(_) => true, |
112 | Data::Struct(_) => false, |
113 | } |
114 | } |
115 | |
116 | /// Returns `true` if this instance is `Data::Struct`. |
117 | pub fn is_struct(&self) -> bool { |
118 | !self.is_enum() |
119 | } |
120 | } |
121 | |
122 | impl<V: FromVariant, F: FromField> Data<V, F> { |
123 | /// Attempt to convert from a `syn::Data` instance. |
124 | pub fn try_from(body: &syn::Data) -> Result<Self> { |
125 | match *body { |
126 | syn::Data::Enum(ref data: &DataEnum) => { |
127 | let mut errors: Accumulator = Error::accumulator(); |
128 | let items: Vec = dataimpl Iterator |
129 | .variants |
130 | .iter() |
131 | .filter_map(|v: &Variant| errors.handle(result:FromVariant::from_variant(v))) |
132 | .collect(); |
133 | |
134 | errors.finish_with(success:Data::Enum(items)) |
135 | } |
136 | syn::Data::Struct(ref data: &DataStruct) => Ok(Data::Struct(Fields::try_from(&data.fields)?)), |
137 | // This deliberately doesn't set a span on the error message, as the error is most useful if |
138 | // applied to the call site of the offending macro. Given that the message is very generic, |
139 | // putting it on the union keyword ends up being confusing. |
140 | syn::Data::Union(_) => Err(Error::custom(msg:"Unions are not supported" )), |
141 | } |
142 | } |
143 | } |
144 | |
145 | impl<V: UsesTypeParams, F: UsesTypeParams> UsesTypeParams for Data<V, F> { |
146 | fn uses_type_params<'a>( |
147 | &self, |
148 | options: &usage::Options, |
149 | type_set: &'a IdentSet, |
150 | ) -> IdentRefSet<'a> { |
151 | match *self { |
152 | Data::Struct(ref v: &Fields) => v.uses_type_params(options, type_set), |
153 | Data::Enum(ref v: &Vec) => v.uses_type_params(options, type_set), |
154 | } |
155 | } |
156 | } |
157 | |
158 | impl<V: UsesLifetimes, F: UsesLifetimes> UsesLifetimes for Data<V, F> { |
159 | fn uses_lifetimes<'a>( |
160 | &self, |
161 | options: &usage::Options, |
162 | lifetimes: &'a LifetimeSet, |
163 | ) -> LifetimeRefSet<'a> { |
164 | match *self { |
165 | Data::Struct(ref v: &Fields) => v.uses_lifetimes(options, lifetimes), |
166 | Data::Enum(ref v: &Vec) => v.uses_lifetimes(options, lifetimes), |
167 | } |
168 | } |
169 | } |
170 | |
171 | /// Equivalent to `syn::Fields`, but replaces the AST element with a generic. |
172 | #[derive (Debug, Clone)] |
173 | pub struct Fields<T> { |
174 | pub style: Style, |
175 | pub fields: Vec<T>, |
176 | span: Option<Span>, |
177 | __nonexhaustive: (), |
178 | } |
179 | |
180 | impl<T> Fields<T> { |
181 | /// Creates a new [`Fields`] struct. |
182 | pub fn new(style: Style, fields: Vec<T>) -> Self { |
183 | Self { |
184 | style, |
185 | fields, |
186 | span: None, |
187 | __nonexhaustive: (), |
188 | } |
189 | } |
190 | |
191 | /// Adds a [`Span`] to [`Fields`]. |
192 | pub fn with_span(mut self, span: Span) -> Self { |
193 | if self.span.is_none() { |
194 | self.span = Some(span); |
195 | } |
196 | self |
197 | } |
198 | |
199 | pub fn empty_from(vd: &syn::Fields) -> Self { |
200 | Self::new(vd.into(), Vec::new()) |
201 | } |
202 | |
203 | /// Splits the `Fields` into its style and fields for further processing. |
204 | /// Returns an empty `Vec` for `Unit` data. |
205 | pub fn split(self) -> (Style, Vec<T>) { |
206 | (self.style, self.fields) |
207 | } |
208 | |
209 | /// Returns true if this variant's data makes it a newtype. |
210 | pub fn is_newtype(&self) -> bool { |
211 | self.style == Style::Tuple && self.len() == 1 |
212 | } |
213 | |
214 | pub fn is_unit(&self) -> bool { |
215 | self.style.is_unit() |
216 | } |
217 | |
218 | pub fn is_tuple(&self) -> bool { |
219 | self.style.is_tuple() |
220 | } |
221 | |
222 | pub fn is_struct(&self) -> bool { |
223 | self.style.is_struct() |
224 | } |
225 | |
226 | pub fn as_ref(&self) -> Fields<&T> { |
227 | Fields { |
228 | style: self.style, |
229 | fields: self.fields.iter().collect(), |
230 | span: self.span, |
231 | __nonexhaustive: (), |
232 | } |
233 | } |
234 | |
235 | pub fn map<F, U>(self, map: F) -> Fields<U> |
236 | where |
237 | F: FnMut(T) -> U, |
238 | { |
239 | Fields { |
240 | style: self.style, |
241 | fields: self.fields.into_iter().map(map).collect(), |
242 | span: self.span, |
243 | __nonexhaustive: (), |
244 | } |
245 | } |
246 | |
247 | pub fn iter(&self) -> slice::Iter<T> { |
248 | self.fields.iter() |
249 | } |
250 | |
251 | /// Returns the number of fields in the structure. |
252 | pub fn len(&self) -> usize { |
253 | self.fields.len() |
254 | } |
255 | |
256 | /// Returns `true` if the `Fields` contains no fields. |
257 | pub fn is_empty(&self) -> bool { |
258 | self.fields.is_empty() |
259 | } |
260 | } |
261 | |
262 | impl<F: FromField> Fields<F> { |
263 | pub fn try_from(fields: &syn::Fields) -> Result<Self> { |
264 | let mut errors = Error::accumulator(); |
265 | let items = { |
266 | match &fields { |
267 | syn::Fields::Named(fields) => fields |
268 | .named |
269 | .iter() |
270 | .filter_map(|field| { |
271 | errors.handle(FromField::from_field(field).map_err(|err| { |
272 | // There should always be an ident here, since this is a collection |
273 | // of named fields, but `syn` doesn't prevent someone from manually |
274 | // constructing an invalid collection so a guard is still warranted. |
275 | if let Some(ident) = &field.ident { |
276 | err.at(ident) |
277 | } else { |
278 | err |
279 | } |
280 | })) |
281 | }) |
282 | .collect(), |
283 | syn::Fields::Unnamed(fields) => fields |
284 | .unnamed |
285 | .iter() |
286 | .filter_map(|field| errors.handle(FromField::from_field(field))) |
287 | .collect(), |
288 | syn::Fields::Unit => vec![], |
289 | } |
290 | }; |
291 | |
292 | errors.finish()?; |
293 | |
294 | Ok(Self::new(fields.into(), items).with_span(fields.span())) |
295 | } |
296 | } |
297 | |
298 | impl<T: ToTokens> ToTokens for Fields<T> { |
299 | fn to_tokens(&self, tokens: &mut TokenStream) { |
300 | let fields = &self.fields; |
301 | // An unknown Span should be `Span::call_site()`; |
302 | // https://docs.rs/syn/1.0.12/syn/spanned/trait.Spanned.html#tymethod.span |
303 | let span = self.span.unwrap_or_else(Span::call_site); |
304 | |
305 | match self.style { |
306 | Style::Struct => { |
307 | let trailing_comma = { |
308 | if fields.is_empty() { |
309 | quote!() |
310 | } else { |
311 | quote!(,) |
312 | } |
313 | }; |
314 | |
315 | tokens.extend(quote_spanned![span => { #(#fields),* #trailing_comma }]); |
316 | } |
317 | Style::Tuple => { |
318 | tokens.extend(quote_spanned![span => ( #(#fields),* )]); |
319 | } |
320 | Style::Unit => {} |
321 | } |
322 | } |
323 | } |
324 | |
325 | impl<T: PartialEq> PartialEq for Fields<T> { |
326 | fn eq(&self, other: &Self) -> bool { |
327 | self.style == other.style && self.fields == other.fields |
328 | } |
329 | } |
330 | |
331 | impl<T: Eq> Eq for Fields<T> {} |
332 | |
333 | impl<T> IntoIterator for Fields<T> { |
334 | type Item = T; |
335 | type IntoIter = vec::IntoIter<T>; |
336 | |
337 | fn into_iter(self) -> Self::IntoIter { |
338 | self.fields.into_iter() |
339 | } |
340 | } |
341 | |
342 | impl<T> From<Style> for Fields<T> { |
343 | fn from(style: Style) -> Self { |
344 | Self::new(style, fields:Vec::new()) |
345 | } |
346 | } |
347 | |
348 | impl<T, U: Into<Vec<T>>> From<(Style, U)> for Fields<T> { |
349 | fn from((style: Style, fields: U): (Style, U)) -> Self { |
350 | style.with_fields(fields) |
351 | } |
352 | } |
353 | |
354 | impl<T: UsesTypeParams> UsesTypeParams for Fields<T> { |
355 | fn uses_type_params<'a>( |
356 | &self, |
357 | options: &usage::Options, |
358 | type_set: &'a IdentSet, |
359 | ) -> IdentRefSet<'a> { |
360 | self.fields.uses_type_params(options, type_set) |
361 | } |
362 | } |
363 | |
364 | impl<T: UsesLifetimes> UsesLifetimes for Fields<T> { |
365 | fn uses_lifetimes<'a>( |
366 | &self, |
367 | options: &usage::Options, |
368 | lifetimes: &'a LifetimeSet, |
369 | ) -> LifetimeRefSet<'a> { |
370 | self.fields.uses_lifetimes(options, lifetimes) |
371 | } |
372 | } |
373 | |
374 | #[derive (Debug, Clone, Copy, PartialEq, Eq)] |
375 | pub enum Style { |
376 | Tuple, |
377 | Struct, |
378 | Unit, |
379 | } |
380 | |
381 | impl Style { |
382 | pub fn is_unit(self) -> bool { |
383 | self == Style::Unit |
384 | } |
385 | |
386 | pub fn is_tuple(self) -> bool { |
387 | self == Style::Tuple |
388 | } |
389 | |
390 | pub fn is_struct(self) -> bool { |
391 | self == Style::Struct |
392 | } |
393 | |
394 | /// Creates a new `Fields` of the specified style with the passed-in fields. |
395 | fn with_fields<T, U: Into<Vec<T>>>(self, fields: U) -> Fields<T> { |
396 | Fields::new(self, fields:fields.into()) |
397 | } |
398 | } |
399 | |
400 | impl From<syn::Fields> for Style { |
401 | fn from(vd: syn::Fields) -> Self { |
402 | (&vd).into() |
403 | } |
404 | } |
405 | |
406 | impl<'a> From<&'a syn::Fields> for Style { |
407 | fn from(vd: &syn::Fields) -> Self { |
408 | match *vd { |
409 | syn::Fields::Named(_) => Style::Struct, |
410 | syn::Fields::Unnamed(_) => Style::Tuple, |
411 | syn::Fields::Unit => Style::Unit, |
412 | } |
413 | } |
414 | } |
415 | |
416 | #[derive (Debug, Clone)] |
417 | pub enum NestedMeta { |
418 | Meta(syn::Meta), |
419 | Lit(syn::Lit), |
420 | } |
421 | |
422 | impl NestedMeta { |
423 | pub fn parse_meta_list(tokens: TokenStream) -> syn::Result<Vec<Self>> { |
424 | syn::punctuated::Punctuated::<NestedMeta, Token![,]>::parse_terminated |
425 | .parse2(tokens) |
426 | .map(|punctuated: Punctuated| punctuated.into_iter().collect()) |
427 | } |
428 | } |
429 | |
430 | impl syn::parse::Parse for NestedMeta { |
431 | fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { |
432 | if input.peek(token:syn::Lit) && !(input.peek(token:syn::LitBool) && input.peek2(Token![=])) { |
433 | input.parse().map(op:NestedMeta::Lit) |
434 | } else if input.peek(token:syn::Ident::peek_any) |
435 | || input.peek(Token![::]) && input.peek3(token:syn::Ident::peek_any) |
436 | { |
437 | input.parse().map(op:NestedMeta::Meta) |
438 | } else { |
439 | Err(input.error(message:"expected identifier or literal" )) |
440 | } |
441 | } |
442 | } |
443 | |
444 | impl ToTokens for NestedMeta { |
445 | fn to_tokens(&self, tokens: &mut TokenStream) { |
446 | match self { |
447 | NestedMeta::Meta(meta: &Meta) => meta.to_tokens(tokens), |
448 | NestedMeta::Lit(lit: &Lit) => lit.to_tokens(tokens), |
449 | } |
450 | } |
451 | } |
452 | |
453 | #[cfg (test)] |
454 | mod tests { |
455 | use super::*; |
456 | |
457 | // it is not possible to directly convert a TokenStream into syn::Fields, so you have |
458 | // to convert the TokenStream into DeriveInput first and then pass the syn::Fields to |
459 | // Fields::try_from. |
460 | fn token_stream_to_fields(input: TokenStream) -> Fields<syn::Field> { |
461 | Fields::try_from(&{ |
462 | if let syn::Data::Struct(s) = syn::parse2::<syn::DeriveInput>(input).unwrap().data { |
463 | s.fields |
464 | } else { |
465 | panic!(); |
466 | } |
467 | }) |
468 | .unwrap() |
469 | } |
470 | |
471 | #[test ] |
472 | fn test_style_eq() { |
473 | // `Fields` implements `Eq` manually, so it has to be ensured, that all fields of `Fields` |
474 | // implement `Eq`, this test would fail, if someone accidentally removed the Eq |
475 | // implementation from `Style`. |
476 | struct _AssertEq |
477 | where |
478 | Style: Eq; |
479 | } |
480 | |
481 | #[test ] |
482 | fn test_fields_to_tokens_struct() { |
483 | let reference = quote!( |
484 | { |
485 | executable: String, |
486 | args: Vec<String>, |
487 | env: Vec<String>, |
488 | index: usize, |
489 | optional: Option<String>, |
490 | current_dir: String, |
491 | } |
492 | ); |
493 | let input = quote!( |
494 | struct ExampleTest #reference |
495 | ); |
496 | |
497 | let fields = token_stream_to_fields(input); |
498 | |
499 | let mut result = quote!(); |
500 | fields.to_tokens(&mut result); |
501 | assert_eq!(result.to_string(), reference.to_string()); |
502 | } |
503 | |
504 | #[test ] |
505 | fn test_fields_to_tokens_tuple() { |
506 | let reference = quote!((u64, usize, &'a T)); |
507 | let input = quote!( |
508 | struct ExampleTest #reference; |
509 | ); |
510 | |
511 | let fields = token_stream_to_fields(input); |
512 | |
513 | let mut result = quote!(); |
514 | fields.to_tokens(&mut result); |
515 | assert_eq!(result.to_string(), reference.to_string()); |
516 | } |
517 | } |
518 | |