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`.
empty_from(src: &syn::Data) -> Self29     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.
try_empty_from(src: &syn::Data) -> Result<Self>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>`.
as_ref(&self) -> 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.
map_enum_variants<T, U>(self, map: T) -> Data<U, F> where T: FnMut(V) -> U,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.
map_struct_fields<T, U>(self, map: T) -> Data<V, U> where T: FnMut(F) -> U,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.
map_struct<T, U>(self, mut map: T) -> Data<V, U> where T: FnMut(Fields<F>) -> Fields<U>,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.
take_struct(self) -> Option<Fields<F>>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.
take_enum(self) -> Option<Vec<V>>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`.
is_enum(&self) -> bool109     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`.
is_struct(&self) -> bool117     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.
try_from(body: &syn::Data) -> Result<Self>124     pub fn try_from(body: &syn::Data) -> Result<Self> {
125         match *body {
126             syn::Data::Enum(ref data) => {
127                 let mut errors = Error::accumulator();
128                 let items = data
129                     .variants
130                     .iter()
131                     .filter_map(|v| errors.handle(FromVariant::from_variant(v)))
132                     .collect();
133 
134                 errors.finish_with(Data::Enum(items))
135             }
136             syn::Data::Struct(ref data) => 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("Unions are not supported")),
141         }
142     }
143 }
144 
145 impl<V: UsesTypeParams, F: UsesTypeParams> UsesTypeParams for Data<V, F> {
uses_type_params<'a>( &self, options: &usage::Options, type_set: &'a IdentSet, ) -> IdentRefSet<'a>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) => v.uses_type_params(options, type_set),
153             Data::Enum(ref v) => v.uses_type_params(options, type_set),
154         }
155     }
156 }
157 
158 impl<V: UsesLifetimes, F: UsesLifetimes> UsesLifetimes for Data<V, F> {
uses_lifetimes<'a>( &self, options: &usage::Options, lifetimes: &'a LifetimeSet, ) -> LifetimeRefSet<'a>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) => v.uses_lifetimes(options, lifetimes),
166             Data::Enum(ref v) => 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.
new(style: Style, fields: Vec<T>) -> Self182     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`].
with_span(mut self, span: Span) -> Self192     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 
empty_from(vd: &syn::Fields) -> Self199     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.
split(self) -> (Style, Vec<T>)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.
is_newtype(&self) -> bool210     pub fn is_newtype(&self) -> bool {
211         self.style == Style::Tuple && self.len() == 1
212     }
213 
is_unit(&self) -> bool214     pub fn is_unit(&self) -> bool {
215         self.style.is_unit()
216     }
217 
is_tuple(&self) -> bool218     pub fn is_tuple(&self) -> bool {
219         self.style.is_tuple()
220     }
221 
is_struct(&self) -> bool222     pub fn is_struct(&self) -> bool {
223         self.style.is_struct()
224     }
225 
as_ref(&self) -> Fields<&T>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 
map<F, U>(self, map: F) -> Fields<U> where F: FnMut(T) -> U,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 
iter(&self) -> slice::Iter<'_, T>247     pub fn iter(&self) -> slice::Iter<'_, T> {
248         self.fields.iter()
249     }
250 
251     /// Returns the number of fields in the structure.
len(&self) -> usize252     pub fn len(&self) -> usize {
253         self.fields.len()
254     }
255 
256     /// Returns `true` if the `Fields` contains no fields.
is_empty(&self) -> bool257     pub fn is_empty(&self) -> bool {
258         self.fields.is_empty()
259     }
260 }
261 
262 impl<F: FromField> Fields<F> {
try_from(fields: &syn::Fields) -> Result<Self>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> {
to_tokens(&self, tokens: &mut TokenStream)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> {
eq(&self, other: &Self) -> bool326     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 
into_iter(self) -> Self::IntoIter337     fn into_iter(self) -> Self::IntoIter {
338         self.fields.into_iter()
339     }
340 }
341 
342 impl<T> From<Style> for Fields<T> {
from(style: Style) -> Self343     fn from(style: Style) -> Self {
344         Self::new(style, Vec::new())
345     }
346 }
347 
348 impl<T, U: Into<Vec<T>>> From<(Style, U)> for Fields<T> {
from((style, fields): (Style, U)) -> Self349     fn from((style, fields): (Style, U)) -> Self {
350         style.with_fields(fields)
351     }
352 }
353 
354 impl<T: UsesTypeParams> UsesTypeParams for Fields<T> {
uses_type_params<'a>( &self, options: &usage::Options, type_set: &'a IdentSet, ) -> IdentRefSet<'a>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> {
uses_lifetimes<'a>( &self, options: &usage::Options, lifetimes: &'a LifetimeSet, ) -> LifetimeRefSet<'a>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 {
is_unit(self) -> bool382     pub fn is_unit(self) -> bool {
383         self == Style::Unit
384     }
385 
is_tuple(self) -> bool386     pub fn is_tuple(self) -> bool {
387         self == Style::Tuple
388     }
389 
is_struct(self) -> bool390     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.
with_fields<T, U: Into<Vec<T>>>(self, fields: U) -> Fields<T>395     fn with_fields<T, U: Into<Vec<T>>>(self, fields: U) -> Fields<T> {
396         Fields::new(self, fields.into())
397     }
398 }
399 
400 impl From<syn::Fields> for Style {
from(vd: syn::Fields) -> Self401     fn from(vd: syn::Fields) -> Self {
402         (&vd).into()
403     }
404 }
405 
406 impl<'a> From<&'a syn::Fields> for Style {
from(vd: &syn::Fields) -> Self407     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 {
parse_meta_list(tokens: TokenStream) -> syn::Result<Vec<Self>>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.into_iter().collect())
427     }
428 }
429 
430 impl syn::parse::Parse for NestedMeta {
parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self>431     fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
432         if input.peek(syn::Lit) && !(input.peek(syn::LitBool) && input.peek2(Token![=])) {
433             input.parse().map(NestedMeta::Lit)
434         } else if input.peek(syn::Ident::peek_any)
435             || input.peek(Token![::]) && input.peek3(syn::Ident::peek_any)
436         {
437             input.parse().map(NestedMeta::Meta)
438         } else {
439             Err(input.error("expected identifier or literal"))
440         }
441     }
442 }
443 
444 impl ToTokens for NestedMeta {
to_tokens(&self, tokens: &mut TokenStream)445     fn to_tokens(&self, tokens: &mut TokenStream) {
446         match self {
447             NestedMeta::Meta(meta) => meta.to_tokens(tokens),
448             NestedMeta::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.
token_stream_to_fields(input: TokenStream) -> Fields<syn::Field>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]
test_style_eq()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]
test_fields_to_tokens_struct()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]
test_fields_to_tokens_tuple()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