1use proc_macro::TokenStream;
2use proc_macro2::Span;
3use quote::quote;
4use syn::parse::Error;
5use syn::spanned::Spanned;
6use syn::{parse_macro_input, DeriveInput, Field, Ident};
7
8#[proc_macro_derive(TracerOpts)]
9pub fn tracer_opts(input: TokenStream) -> TokenStream {
10 let input = parse_macro_input!(input as DeriveInput);
11 match impl_tracer_opts(&input) {
12 Ok(output) => output,
13 Err(error) => error.to_compile_error().into(),
14 }
15}
16
17fn impl_tracer_opts(ast: &syn::DeriveInput) -> Result<TokenStream, Error> {
18 let data = match &ast.data {
19 syn::Data::Struct(data) => match &data.fields {
20 syn::Fields::Named(fields) => {
21 let ident = &ast.ident;
22 let mut methods = Vec::new();
23 for field in fields.named.iter() {
24 let ident = field.ident.as_ref().ok_or_else(|| {
25 Error::new(
26 ast.span(),
27 "TracerOpts is not supported fields of tuple structs",
28 )
29 })?;
30 match extract_opt_type(field) {
31 Some((cont, ty)) if cont == "Option" => {
32 methods.push(quote! {
33 pub fn #ident(mut self, value: impl Into<#ty>) -> Self {
34 self.#ident = Some(value.into());
35 self
36 }
37 });
38 let set_ident =
39 Ident::new(&format!("set_{}", ident), Span::call_site());
40 methods.push(quote! {
41 pub fn #set_ident(&mut self, value: impl Into<#ty>) -> &mut Self {
42 self.#ident = Some(value.into());
43 self
44 }
45 });
46 }
47 Some((cont, ty)) if cont == "Vec" => {
48 methods.push(quote! {
49 pub fn #ident<T>(mut self, values: impl IntoIterator<Item = T>) -> Self
50 where
51 #ty: From<T>,
52 {
53 self.#ident.extend(values.into_iter().map(<#ty>::from));
54 self
55 }
56 });
57 let single = ident.to_string();
58 let mut chars = single.chars();
59 chars.next_back();
60 let single_ident = Ident::new(chars.as_str(), Span::call_site());
61 methods.push(quote! {
62 pub fn #single_ident(mut self, value: impl Into<#ty>) -> Self {
63 self.#ident.push(value.into());
64 self
65 }
66 });
67 }
68 _ => {
69 }
73 }
74 }
75 quote! {
76 impl #ident {
77 #( #methods )*
78 }
79 }
80 }
81 syn::Fields::Unnamed(_) => {
82 return Err(Error::new(
83 ast.span(),
84 "TracerOpts is not supported for tuple structs",
85 ))
86 }
87 syn::Fields::Unit => {
88 return Err(Error::new(
89 ast.span(),
90 "TracerOpts is not supported for unit structs",
91 ))
92 }
93 },
94 syn::Data::Enum(_) => {
95 return Err(Error::new(
96 ast.span(),
97 "TracerOpts is not supported for enums",
98 ))
99 }
100 syn::Data::Union(_) => {
101 return Err(Error::new(
102 ast.span(),
103 "TracerOpts is not supported for unions",
104 ))
105 }
106 };
107 Ok(data.into())
108}
109
110fn extract_opt_type(field: &Field) -> Option<(&syn::Ident, &syn::Type)> {
111 let path = if let syn::Type::Path(type_path) = &field.ty {
112 if type_path.qself.is_some() {
113 return None;
114 } else {
115 &type_path.path
116 }
117 } else {
118 return None;
119 };
120 let segment = path.segments.last()?;
121 let generic_params =
127 if let syn::PathArguments::AngleBracketed(generic_params) = &segment.arguments {
128 generic_params
129 } else {
130 return None;
131 };
132 if let syn::GenericArgument::Type(ty) = generic_params.args.first()? {
133 Some((&segment.ident, ty))
134 } else {
135 None
136 }
137}