11use proc_macro:: TokenStream ;
22
3+ use proc_macro2:: TokenStream as TokenStream2 ;
34use quote:: { quote, ToTokens } ;
45use syn:: parse:: { Parse , ParseStream } ;
6+ use syn:: spanned:: Spanned ;
57use syn:: * ;
68
79/// The non exhaustive version of `PartialDebug`
@@ -54,70 +56,158 @@ pub fn derive_non_exhaustive(input: TokenStream) -> TokenStream {
5456/// The placeholder version of `PartialDebug`
5557#[ proc_macro_derive( PlaceholderPartialDebug , attributes( debug_placeholder) ) ]
5658pub fn derive_placeholder ( input : TokenStream ) -> TokenStream {
57- let input = parse_macro_input ! ( input as ItemStruct ) ;
59+ let input = parse_macro_input ! ( input as DeriveInput ) ;
5860 let placeholder = match get_placeholder ( & input) {
5961 Ok ( placeholder) => placeholder,
6062 Err ( err) => {
6163 return err. to_compile_error ( ) . into ( ) ;
6264 }
6365 } ;
6466
65- let name = & input. ident ;
67+ let name = input. ident ;
6668 let ( impl_generics, ty_generics, where_clause) = input. generics . split_for_impl ( ) ;
6769
68- let no_fields = punctuated:: Punctuated :: new ( ) ;
70+ let implementation = match input. data {
71+ Data :: Struct ( DataStruct { fields, .. } ) => gen_variant_debug (
72+ & fields,
73+ & name,
74+ struct_field_conversions ( & fields, & placeholder) ,
75+ ) ,
76+ Data :: Enum ( data_enum) => gen_enum_debug ( & data_enum, & name, & placeholder) ,
77+ Data :: Union ( _) => unimplemented ! ( ) ,
78+ } ;
6979
70- let ( fields, constructor) = match & input. fields {
71- Fields :: Named ( FieldsNamed { named, .. } ) => ( named, quote ! { debug_struct} ) ,
72- Fields :: Unnamed ( FieldsUnnamed { unnamed, .. } ) => ( unnamed, quote ! { debug_tuple} ) ,
73- Fields :: Unit => ( & no_fields, quote ! { debug_tuple} ) ,
80+ let expanded = quote ! {
81+ impl #impl_generics :: core:: fmt:: Debug for #name #ty_generics #where_clause{
82+ fn fmt( & self , f: & mut :: core:: fmt:: Formatter <' _>) -> :: core:: fmt:: Result {
83+ #implementation
84+ }
85+ }
7486 } ;
7587
76- let as_debug_all_fields = fields. iter ( ) . enumerate ( ) . map ( |( idx, field) | {
77- let type_name = get_type_name ( & field. ty ) ;
88+ TokenStream :: from ( expanded)
89+ }
90+
91+ fn gen_variant_debug (
92+ fields : & Fields ,
93+ variant_name : & Ident ,
94+ field_conversions : impl Iterator < Item = TokenStream2 > ,
95+ ) -> TokenStream2 {
96+ let constructor = match fields {
97+ Fields :: Named ( _) => quote ! { debug_struct} ,
98+ Fields :: Unnamed ( _) | Fields :: Unit => quote ! { debug_tuple} ,
99+ } ;
78100
79- // type name or given placeholder string
80- let placeholder_string = placeholder. as_ref ( ) . unwrap_or ( & type_name) ;
101+ quote ! {
102+ f. #constructor( stringify!( #variant_name) )
103+ #( #field_conversions) *
104+ . finish( )
105+ }
106+ }
81107
82- match & field. ident {
83- None => {
84- let idx = Index :: from ( idx) ;
85- quote ! {
86- . field(
87- match :: partialdebug:: AsDebug :: as_debug( & self . #idx) {
88- None => & :: partialdebug:: Placeholder ( #placeholder_string) ,
89- Some ( __field) => __field,
90- } ,
91- )
92- }
93- }
94- Some ( name) => {
95- quote ! {
96- . field(
97- stringify!( #name) ,
98- match :: partialdebug:: AsDebug :: as_debug( & self . #name) {
99- None => & :: partialdebug:: Placeholder ( #placeholder_string) ,
100- Some ( __field) => __field,
101- } ,
102- )
103- }
108+ fn gen_enum_debug (
109+ data_enum : & DataEnum ,
110+ enum_name : & Ident ,
111+ placeholder : & Option < String > ,
112+ ) -> TokenStream2 {
113+ let all_variants = data_enum. variants . iter ( ) . map ( |variant| {
114+ let variant_name = & variant. ident ;
115+ let match_content = gen_variant_debug (
116+ & variant. fields ,
117+ variant_name,
118+ enum_field_conversions ( & variant. fields , placeholder) ,
119+ ) ;
120+ let match_pattern = gen_match_pattern ( enum_name, variant) ;
121+ quote ! {
122+ #match_pattern => {
123+ #match_content
104124 }
105125 }
106126 } ) ;
107127
108- let expanded = quote ! {
109- impl #impl_generics :: core:: fmt:: Debug for #name #ty_generics #where_clause{
110- fn fmt( & self , f: & mut :: core:: fmt:: Formatter <' _>) -> :: core:: fmt:: Result {
111- f. #constructor( stringify!( #name) )
128+ quote ! {
129+ match self {
130+ #( #all_variants) *
131+ }
132+ }
133+ }
112134
113- #( #as_debug_all_fields) *
135+ fn struct_field_conversions < ' a > (
136+ fields : & ' a Fields ,
137+ placeholder : & ' a Option < String > ,
138+ ) -> impl Iterator < Item = TokenStream2 > + ' a {
139+ fields. iter ( ) . enumerate ( ) . map ( move |( idx, field) | {
140+ let ( field_handle, name_arg) = match & field. ident {
141+ None => {
142+ let index = Index :: from ( idx) ;
143+ ( quote ! { self . #index} , None )
144+ }
145+ Some ( name) => ( quote ! { self . #name} , Some ( quote ! { stringify!( #name) , } ) ) ,
146+ } ;
147+ gen_field_as_debug ( field, placeholder, field_handle, name_arg)
148+ } )
149+ }
114150
115- . finish( )
151+ fn enum_field_conversions < ' a > (
152+ fields : & ' a Fields ,
153+ placeholder : & ' a Option < String > ,
154+ ) -> impl Iterator < Item = TokenStream2 > + ' a {
155+ fields. iter ( ) . enumerate ( ) . map ( move |( idx, field) | {
156+ let ( field_handle, name_arg) = match & field. ident {
157+ None => {
158+ let ident = Ident :: new ( & format ! ( "__{}" , idx) , field. span ( ) ) ;
159+ ( quote ! { #ident} , None )
160+ }
161+ Some ( name) => ( quote ! { #name} , Some ( quote ! { stringify!( #name) , } ) ) ,
162+ } ;
163+ gen_field_as_debug ( field, placeholder, field_handle, name_arg)
164+ } )
165+ }
166+
167+ fn gen_field_as_debug (
168+ field : & Field ,
169+ placeholder : & Option < String > ,
170+ field_handle : TokenStream2 ,
171+ name_arg : Option < TokenStream2 > ,
172+ ) -> TokenStream2 {
173+ let type_name = get_type_name ( & field. ty ) ;
174+
175+ // type name or given placeholder string
176+ let placeholder_string = placeholder. as_ref ( ) . unwrap_or ( & type_name) ;
177+
178+ quote ! {
179+ . field(
180+ #name_arg
181+ match :: partialdebug:: AsDebug :: as_debug( & #field_handle) {
182+ None => & :: partialdebug:: Placeholder ( #placeholder_string) ,
183+ Some ( __field) => __field,
184+ } ,
185+ )
186+ }
187+ }
188+
189+ fn gen_match_pattern ( enum_name : & Ident , variant : & Variant ) -> TokenStream2 {
190+ let variant_name = & variant. ident ;
191+ let destructuring_pattern = match & variant. fields {
192+ Fields :: Named ( FieldsNamed { named, .. } ) => {
193+ let patterns = named. iter ( ) . map ( |field| & field. ident ) ;
194+ quote ! {
195+ { #( #patterns) , * }
196+ }
197+ }
198+ Fields :: Unnamed ( FieldsUnnamed { unnamed, .. } ) => {
199+ let patterns = unnamed
200+ . iter ( )
201+ . enumerate ( )
202+ . map ( |( idx, field) | Ident :: new ( & format ! ( "__{}" , idx) , field. span ( ) ) ) ;
203+ quote ! {
204+ ( #( #patterns) , * )
116205 }
117206 }
207+ Fields :: Unit => TokenStream2 :: new ( ) ,
118208 } ;
119209
120- TokenStream :: from ( expanded )
210+ quote ! { #enum_name :: #variant_name #destructuring_pattern }
121211}
122212
123213struct Placeholder ( String ) ;
@@ -130,7 +220,7 @@ impl Parse for Placeholder {
130220}
131221
132222/// Tries to parse a placeholder string if there is one
133- fn get_placeholder ( input : & ItemStruct ) -> Result < Option < String > > {
223+ fn get_placeholder ( input : & DeriveInput ) -> Result < Option < String > > {
134224 let placeholders: Vec < _ > = input
135225 . attrs
136226 . iter ( )
0 commit comments