| // The use of fields in debug print commands does not count as "used", |
| // which causes the fields to trigger an unwanted dead code warning. |
| #![allow(dead_code)] |
| |
| //! This example shows how to do struct and field parsing using darling. |
| |
| use darling::{ast, FromDeriveInput, FromField, FromMeta}; |
| use proc_macro2::TokenStream; |
| use quote::{quote, ToTokens}; |
| use syn::parse_str; |
| |
| /// A speaking volume. Deriving `FromMeta` will cause this to be usable |
| /// as a string value for a meta-item key. |
| #[derive(Debug, Clone, Copy, FromMeta)] |
| #[darling(default)] |
| enum Volume { |
| Normal, |
| Whisper, |
| Shout, |
| } |
| |
| impl Default for Volume { |
| fn default() -> Self { |
| Volume::Normal |
| } |
| } |
| |
| /// Support parsing from a full derive input. Unlike FromMeta, this isn't |
| /// composable; each darling-dependent crate should have its own struct to handle |
| /// when its trait is derived. |
| #[derive(Debug, FromDeriveInput)] |
| // This line says that we want to process all attributes declared with `my_trait`, |
| // and that darling should panic if this receiver is given an enum. |
| #[darling(attributes(my_trait), supports(struct_any))] |
| struct MyInputReceiver { |
| /// The struct ident. |
| ident: syn::Ident, |
| |
| /// The type's generics. You'll need these any time your trait is expected |
| /// to work with types that declare generics. |
| generics: syn::Generics, |
| |
| /// Receives the body of the struct or enum. We don't care about |
| /// struct fields because we previously told darling we only accept structs. |
| data: ast::Data<(), MyFieldReceiver>, |
| |
| /// The Input Receiver demands a volume, so use `Volume::Normal` if the |
| /// caller doesn't provide one. |
| #[darling(default)] |
| volume: Volume, |
| } |
| |
| impl ToTokens for MyInputReceiver { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| let MyInputReceiver { |
| ref ident, |
| ref generics, |
| ref data, |
| volume, |
| } = *self; |
| |
| let (imp, ty, wher) = generics.split_for_impl(); |
| let fields = data |
| .as_ref() |
| .take_struct() |
| .expect("Should never be enum") |
| .fields; |
| |
| // Generate the format string which shows each field and its name |
| let fmt_string = fields |
| .iter() |
| .enumerate() |
| .map(|(i, f)| { |
| // We have to preformat the ident in this case so we can fall back |
| // to the field index for unnamed fields. It's not easy to read, |
| // unfortunately. |
| format!( |
| "{} = {{}}", |
| f.ident |
| .as_ref() |
| .map(|v| format!("{}", v)) |
| .unwrap_or_else(|| format!("{}", i)) |
| ) |
| }) |
| .collect::<Vec<_>>() |
| .join(", "); |
| |
| // Generate the actual values to fill the format string. |
| let field_list = fields |
| .into_iter() |
| .enumerate() |
| .map(|(i, f)| { |
| let field_volume = f.volume.unwrap_or(volume); |
| |
| // This works with named or indexed fields, so we'll fall back to the index so we can |
| // write the output as a key-value pair. |
| let field_ident = f.ident |
| .as_ref() |
| .map(|v| quote!(#v)) |
| .unwrap_or_else(|| { |
| let i = syn::Index::from(i); |
| quote!(#i) |
| }); |
| |
| match field_volume { |
| Volume::Normal => quote!(self.#field_ident), |
| Volume::Shout => { |
| quote!(::std::string::ToString::to_string(&self.#field_ident).to_uppercase()) |
| } |
| Volume::Whisper => { |
| quote!(::std::string::ToString::to_string(&self.#field_ident).to_lowercase()) |
| } |
| } |
| }) |
| .collect::<Vec<_>>(); |
| |
| tokens.extend(quote! { |
| impl #imp Speak for #ident #ty #wher { |
| fn speak(&self, writer: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { |
| write!(writer, #fmt_string, #(#field_list),*) |
| } |
| } |
| }); |
| } |
| } |
| |
| #[derive(Debug, FromField)] |
| #[darling(attributes(my_trait))] |
| struct MyFieldReceiver { |
| /// Get the ident of the field. For fields in tuple or newtype structs or |
| /// enum bodies, this can be `None`. |
| ident: Option<syn::Ident>, |
| |
| /// This magic field name pulls the type from the input. |
| ty: syn::Type, |
| |
| /// We declare this as an `Option` so that during tokenization we can write |
| /// `field.volume.unwrap_or(derive_input.volume)` to facilitate field-level |
| /// overrides of struct-level settings. |
| /// |
| /// Because this field is an `Option`, we don't need to include `#[darling(default)]` |
| volume: Option<Volume>, |
| } |
| |
| fn main() { |
| let input = r#"#[derive(MyTrait)] |
| #[my_trait(volume = "shout")] |
| pub struct Foo { |
| #[my_trait(volume = "whisper")] |
| bar: bool, |
| |
| baz: i64, |
| }"#; |
| |
| let parsed = parse_str(input).unwrap(); |
| let receiver = MyInputReceiver::from_derive_input(&parsed).unwrap(); |
| let tokens = quote!(#receiver); |
| |
| println!( |
| r#" |
| INPUT: |
| |
| {} |
| |
| PARSED AS: |
| |
| {:?} |
| |
| EMITS: |
| |
| {} |
| "#, |
| input, receiver, tokens |
| ); |
| } |