jacklog_macro/
lib.rs

1use proc_macro::TokenStream;
2use quote::quote;
3use syn::{
4    parse_macro_input, Data::Struct, DataStruct, DeriveInput, Fields::Named,
5    FieldsNamed,
6};
7
8#[proc_macro_attribute]
9pub fn verbose(_attr: TokenStream, item: TokenStream) -> TokenStream {
10    let ast = parse_macro_input!(item as DeriveInput);
11    let vis = ast.vis;
12    let name = ast.ident;
13    let attrs = &ast.attrs;
14
15    // Get the named fields out of the struct.
16    let Struct(DataStruct {
17        fields:
18            Named(FieldsNamed {
19                ref named,
20                ..
21            }),
22        ..
23    }) = ast.data
24    else {
25        unimplemented!("only works on structs");
26    };
27
28    // Go through all the fields and collect them so we can output them again.
29    let builder_fields = named.iter().map(|f| {
30        let name = &f.ident;
31        let ty = &f.ty;
32        let attrs = &f.attrs;
33        let vis = &f.vis;
34
35        quote! {
36            #(#attrs)*
37            #vis #name: #ty
38        }
39    });
40
41    // Re-output the original struct, but with the added verbose field.
42    let decorated = quote! {
43        #(#attrs)*
44        #vis struct #name {
45            /// Increase verbosity from the baseline of WARN.
46            ///
47            /// Passing additional -v arguments will continue increasing
48            /// verbosity. For more sophisticated logging requirements, use the
49            /// RUST_LOG environment variable, which will override this setting.
50            #[clap(short, long, action = ::clap::ArgAction::Count, global = true)]
51            // TODO: accept a parameter for visibility.
52            #vis verbose: u8,
53
54            #(#builder_fields,)*
55        }
56    };
57
58    decorated.into()
59}