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