| 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.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 From<&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 | |