1use syn::{Data, Fields, GenericParam};
27use quote::{quote, ToTokens};
28
29#[proc_macro_derive(ShallowDebug)]
36pub fn derive_shallow_debug(stream: proc_macro::TokenStream) -> proc_macro::TokenStream {
37 let input = syn::parse_macro_input!(stream as syn::DeriveInput);
38
39 let ident = &input.ident;
40 let fmt_body = match &input.data {
41 Data::Enum(data_enum) => {
42 let variants = data_enum.variants.iter()
43 .map(|variant| {
44 let variant_ident = &variant.ident;
45 match &variant.fields {
46 Fields::Named(_) => {
47 let fmt = format!("{ident}::{variant_ident}{{{{..}}}}");
48 quote!(#ident::#variant_ident{..} => write!(f, #fmt))
49 }
50 Fields::Unnamed(_) => {
51 let fmt = format!("{ident}::{variant_ident}(..)");
52 quote!(#ident::#variant_ident(..) => write!(f, #fmt))
53 }
54 Fields::Unit => {
55 let fmt = format!("{ident}::{variant_ident}");
56 quote!(#ident::#variant_ident => write!(f, #fmt))
57 }
58 }
59 });
60
61 quote! {
62 match self {
63 #(#variants,)*
64 }
65 }
66 }
67 Data::Struct(data_struct) => match &data_struct.fields {
68 Fields::Named(_) => {
69 let fmt = format!("{ident}{{{{..}}}}");
70 quote!(write!(f, #fmt))
71 }
72 Fields::Unnamed(_) => {
73 let fmt = format!("{ident}(..)");
74 quote!(write!(f, #fmt))
75 }
76 Fields::Unit => {
77 let fmt = format!("{ident}");
78 quote!(write!(f, #fmt))
79 }
80 }
81
82 Data::Union(_) => {
83 let fmt = format!("{ident}");
84 quote!(write!(f, #fmt))
85 }
86 };
87
88 let bounds = input.generics.params.iter()
89 .filter_map(|param| match param {
90 GenericParam::Lifetime(lifetime) if lifetime.bounds.is_empty() => None,
91 GenericParam::Lifetime(lifetime) => {
92 let bounds = &lifetime.bounds;
93 let ident = &lifetime.lifetime;
94 Some(quote!(#ident: #bounds))
95 }
96 GenericParam::Type(ty) if ty.bounds.is_empty() => None,
97 GenericParam::Type(ty) => {
98 let bounds = &ty.bounds;
99 let ident = &ty.ident;
100 Some(quote!(#ident: #bounds))
101 }
102 GenericParam::Const(_) => None,
103 })
104 .chain({
105 input.generics.where_clause.iter()
106 .flat_map(|clause| clause.predicates.iter().map(ToTokens::to_token_stream))
107 });
108
109 let ty_vars = input.generics.params.iter()
110 .map(|param| match param {
111 GenericParam::Lifetime(lifetime) => lifetime.lifetime.to_token_stream(),
112 GenericParam::Type(ty) => {
113 let ident = &ty.ident;
114 if let Some(default) = &ty.default {
115 quote!(#ident = #default)
116 } else {
117 ident.to_token_stream()
118 }
119 }
120 GenericParam::Const(cons) => cons.to_token_stream(),
121 })
122 .collect::<Vec<_>>();
123
124 let impl_debug = if ty_vars.is_empty() {
126 quote! {
127 impl std::fmt::Debug for #ident
128 }
129 } else {
130 quote! {
131 impl<#(#ty_vars),*> std::fmt::Debug for #ident<#(#ty_vars),*>
132 where
133 #(#bounds),*
134 }
135 };
136
137 quote! {
138 #impl_debug {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 #fmt_body
141 }
142 }
143 }.into()
144}
145