1- use itertools :: Itertools ;
1+ use darling :: FromMeta ;
22use proc_macro2:: TokenStream ;
33use quote:: quote;
4- use syn:: spanned:: Spanned ;
5- use syn:: { parse_str, Fields , Ident , Lit , Meta , NestedMeta , Path , Result } ;
4+ use syn:: { Fields , Result } ;
65use synstructure:: { decl_derive, AddBounds , BindingInfo , Structure , VariantInfo } ;
76
8- use crate :: filter_ext :: FilterExt ;
9- use crate :: macros :: { bail , error } ;
7+ use crate :: field_attributes :: { DebugFormat , FieldAttributes } ;
8+ use crate :: filter_ext :: RetainExt ;
109use crate :: result_into_stream_ext:: ResultIntoStreamExt ;
1110
11+ mod field_attributes;
1212mod filter_ext;
13- mod macros;
1413mod result_into_stream_ext;
1514#[ cfg( test) ]
1615mod tests;
@@ -37,24 +36,10 @@ fn custom_debug_derive(mut structure: Structure) -> Result<TokenStream> {
3736}
3837
3938fn filter_out_skipped_fields ( structure : & mut Structure ) -> Result < ( ) > {
40- let skip_ident: Ident = parse_str ( "skip" ) . unwrap ( ) ;
41-
42- structure. try_filter ( |binding| {
43- for meta in get_custom_debug_metas ( binding) {
44- let meta = meta?;
45-
46- if let NestedMeta :: Meta ( Meta :: Path ( ref path) ) = meta {
47- if path
48- . get_ident ( )
49- . map ( |ident| ident == & skip_ident)
50- . unwrap_or ( false )
51- {
52- return Ok ( false ) ;
53- }
54- }
55- }
39+ structure. try_retain ( |binding| {
40+ let field_attributes = parse_field_attributes ( binding) ?;
5641
57- Ok ( true )
42+ Ok ( !field_attributes . skip )
5843 } ) ?;
5944
6045 Ok ( ( ) )
@@ -82,20 +67,8 @@ fn generate_match_arm_body(variant: &VariantInfo) -> Result<TokenStream> {
8267}
8368
8469fn generate_debug_builder_call ( binding : & BindingInfo ) -> Result < TokenStream > {
85- let mut format = None ;
86-
87- for meta in get_custom_debug_metas ( binding) {
88- let meta = meta?;
89-
90- match meta {
91- NestedMeta :: Meta ( Meta :: NameValue ( nv) ) => {
92- format = Some ( generate_name_value_builder_call ( binding, nv) ?)
93- }
94- _ => bail ! ( meta. span( ) , "Unsupported attribute" ) ,
95- }
96- }
97-
98- let format = format. unwrap_or_else ( || quote ! { #binding } ) ;
70+ let field_attributes = parse_field_attributes ( binding) ?;
71+ let format = generate_debug_impl ( binding, & field_attributes. debug_format ) ;
9972
10073 let debug_builder_call =
10174 if let Some ( ref name) = binding. ast ( ) . ident . as_ref ( ) . map ( <_ >:: to_string) {
@@ -111,70 +84,44 @@ fn generate_debug_builder_call(binding: &BindingInfo) -> Result<TokenStream> {
11184 Ok ( debug_builder_call)
11285}
11386
114- fn generate_name_value_builder_call (
115- binding : & BindingInfo ,
116- nv : syn:: MetaNameValue ,
117- ) -> Result < TokenStream > {
118- let key_span = nv. path . span ( ) ;
119- let value_span = nv. lit . span ( ) ;
120- let value = nv. lit ;
121- let ident = nv
122- . path
123- . get_ident ( )
124- . map ( Ident :: to_string)
125- . ok_or_else ( || error ! ( key_span, "Unsupported attribute" ) ) ?;
126-
127- match & * ident {
128- "format" => Ok ( quote ! { & format_args!( #value, #binding) } ) ,
129- "with" => match value {
130- Lit :: Str ( fun) => {
131- let fun = fun
132- . parse :: < Path > ( )
133- . map_err ( |_| error ! ( fun. span( ) , "Invalid path to function" ) ) ?;
134-
135- Ok ( quote ! {
136- {
137- struct DebugWith <' a, T : ' a> {
138- data: & ' a T ,
139- fmt: fn ( & T , & mut :: core:: fmt:: Formatter ) -> :: core:: fmt:: Result ,
140- }
141-
142- impl <' a, T : ' a> :: core:: fmt:: Debug for DebugWith <' a, T > {
143- fn fmt( & self , fmt: & mut :: core:: fmt:: Formatter ) -> :: core:: fmt:: Result {
144- ( self . fmt) ( self . data, fmt)
145- }
146- }
147-
148- & DebugWith {
149- data: #binding,
150- fmt: #fun,
151- }
87+ fn generate_debug_impl ( binding : & BindingInfo , debug_format : & DebugFormat ) -> TokenStream {
88+ match debug_format {
89+ DebugFormat :: Default => quote ! { #binding } ,
90+ DebugFormat :: Format ( format) => quote ! { & format_args!( #format, #binding) } ,
91+ DebugFormat :: With ( with) => quote ! {
92+ {
93+ struct DebugWith <' a, T : ' a> {
94+ data: & ' a T ,
95+ fmt: fn ( & T , & mut :: core:: fmt:: Formatter ) -> :: core:: fmt:: Result ,
96+ }
97+
98+ impl <' a, T : ' a> :: core:: fmt:: Debug for DebugWith <' a, T > {
99+ fn fmt( & self , fmt: & mut :: core:: fmt:: Formatter ) -> :: core:: fmt:: Result {
100+ ( self . fmt) ( self . data, fmt)
152101 }
153- } )
102+ }
103+
104+ & DebugWith {
105+ data: #binding,
106+ fmt: #with,
107+ }
154108 }
155- _ => bail ! ( value_span, "Invalid `with` value" ) ,
156109 } ,
157- _ => bail ! ( key_span, "Unsupported attribute" ) ,
158110 }
159111}
160112
161- fn get_custom_debug_metas < ' a > (
162- binding : & BindingInfo < ' a > ,
163- ) -> impl Iterator < Item = Result < NestedMeta > > + ' a {
164- let debug_attr = parse_str :: < Path > ( "debug" ) . unwrap ( ) ;
113+ fn parse_field_attributes ( binding : & BindingInfo < ' _ > ) -> Result < FieldAttributes > {
114+ let mut combined_field_attributes = FieldAttributes :: default ( ) ;
165115
166- binding
167- . ast ( )
168- . attrs
169- . iter ( )
170- . filter ( move |attr| attr. path == debug_attr)
171- . map ( |attr| {
172- let meta = attr. parse_meta ( ) ?;
116+ for attr in & binding. ast ( ) . attrs {
117+ if !attr. path ( ) . is_ident ( "debug" ) {
118+ continue ;
119+ }
173120
174- match meta {
175- Meta :: List ( list ) => Ok ( list . nested ) ,
176- _ => bail ! ( meta . span ( ) , "Unsupported attribute style, use `debug(…)`" ) ,
177- }
178- } )
179- . flatten_ok ( )
121+ let field_attributes = FieldAttributes :: from_meta ( & attr . meta ) ? ;
122+
123+ combined_field_attributes = combined_field_attributes . try_combine ( field_attributes ) ? ;
124+ }
125+
126+ Ok ( combined_field_attributes )
180127}
0 commit comments