| 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<'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 |  | 
|---|