1 //! This example shows how to do struct and field parsing using darling.
2 
3 #[macro_use]
4 extern crate darling;
5 extern crate proc_macro2;
6 #[macro_use]
7 extern crate quote;
8 extern crate syn;
9 
10 use darling::ast;
11 use darling::FromDeriveInput;
12 use proc_macro2::TokenStream;
13 use quote::ToTokens;
14 use syn::parse_str;
15 
16 /// A speaking volume. Deriving `FromMeta` will cause this to be usable
17 /// as a string value for a meta-item key.
18 #[derive(Debug, Clone, Copy, FromMeta)]
19 #[darling(default)]
20 enum Volume {
21     Normal,
22     Whisper,
23     Shout,
24 }
25 
26 impl Default for Volume {
27     fn default() -> Self {
28         Volume::Normal
29     }
30 }
31 
32 /// Support parsing from a full derive input. Unlike FromMeta, this isn't
33 /// composable; each darling-dependent crate should have its own struct to handle
34 /// when its trait is derived.
35 #[derive(Debug, FromDeriveInput)]
36 // This line says that we want to process all attributes declared with `my_trait`,
37 // and that darling should panic if this receiver is given an enum.
38 #[darling(attributes(my_trait), supports(struct_any))]
39 struct MyInputReceiver {
40     /// The struct ident.
41     ident: syn::Ident,
42 
43     /// The type's generics. You'll need these any time your trait is expected
44     /// to work with types that declare generics.
45     generics: syn::Generics,
46 
47     /// Receives the body of the struct or enum. We don't care about
48     /// struct fields because we previously told darling we only accept structs.
49     data: ast::Data<(), MyFieldReceiver>,
50 
51     /// The Input Receiver demands a volume, so use `Volume::Normal` if the
52     /// caller doesn't provide one.
53     #[darling(default)]
54     volume: Volume,
55 }
56 
57 impl ToTokens for MyInputReceiver {
58     fn to_tokens(&self, tokens: &mut TokenStream) {
59         let MyInputReceiver {
60             ref ident,
61             ref generics,
62             ref data,
63             volume,
64         } = *self;
65 
66         let (imp, ty, wher) = generics.split_for_impl();
67         let fields = data
68             .as_ref()
69             .take_struct()
70             .expect("Should never be enum")
71             .fields;
72 
73         // Generate the format string which shows each field and its name
74         let fmt_string = fields
75             .iter()
76             .enumerate()
G_DEFINE_TYPE(PpJobsDialog,pp_jobs_dialog,GTK_TYPE_DIALOG)77             .map(|(i, f)| {
78                 // We have to preformat the ident in this case so we can fall back
79                 // to the field index for unnamed fields. It's not easy to read,
80                 // unfortunately.
81                 format!(
82                     "{} = {{}}",
83                     f.ident
84                         .as_ref()
85                         .map(|v| format!("{}", v))
86                         .unwrap_or_else(|| format!("{}", i))
87                 )
88             })
89             .collect::<Vec<_>>()
90             .join(", ");
91 
92         // Generate the actual values to fill the format string.
93         let field_list = fields
94             .into_iter()
95             .enumerate()
is_domain_required(PpJobsDialog * self)96             .map(|(i, f)| {
97                 let field_volume = f.volume.unwrap_or(volume);
98 
99                 // This works with named or indexed fields, so we'll fall back to the index so we can
100                 // write the output as a key-value pair.
101                 let field_ident = f.ident
102                     .as_ref()
103                     .map(|v| quote!(#v))
104                     .unwrap_or_else(|| quote!(#i));
105 
106                 match field_volume {
107                     Volume::Normal => quote!(self.#field_ident),
108                     Volume::Shout => {
109                         quote!(::std::string::ToString::to_string(&self.#field_ident).to_uppercase())
110                     }
111                     Volume::Whisper => {
112                         quote!(::std::string::ToString::to_string(&self.#field_ident).to_lowercase())
113                     }
114                 }
115             })
116             .collect::<Vec<_>>();
117 
118         tokens.extend(quote! {
119             impl #imp Speak for #ident #ty #wher {
120                 fn speak(&self, writer: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
121                     write!(#fmt_string, #(#field_list),*)
122                 }
123             }
124         });
125     }
126 }
127 
128 #[derive(Debug, FromField)]
129 #[darling(attributes(my_trait))]
130 struct MyFieldReceiver {
131     /// Get the ident of the field. For fields in tuple or newtype structs or
132     /// enum bodies, this can be `None`.
133     ident: Option<syn::Ident>,
134 
135     /// This magic field name pulls the type from the input.
136     ty: syn::Type,
auth_entries_changed(PpJobsDialog * self)137 
138     /// We declare this as an `Option` so that during tokenization we can write
139     /// `field.volume.unwrap_or(derive_input.volume)` to facilitate field-level
140     /// overrides of struct-level settings.
141     #[darling(default)]
142     volume: Option<Volume>,
143 }
144 
145 fn main() {
146     let input = r#"#[derive(MyTrait)]
147 #[my_trait(volume = "shout")]
148 pub struct Foo {
149     #[my_trait(volume = "whisper")]
150     bar: bool,
151 
152     baz: i64,
153 }"#;
154 
155     let parsed = parse_str(input).unwrap();
156     let receiver = MyInputReceiver::from_derive_input(&parsed).unwrap();
157     let tokens = quote!(#receiver);
158 
159     println!(
160         r#"
161 INPUT:
162 
163 {}
164 
165 PARSED AS:
166 
167 {:?}
168 
169 EMITS:
170 
171 {}
172     "#,
173         input, receiver, tokens
174     );
175 }
176