@@ -73,8 +73,7 @@ pub fn derive_placeholder(input: TokenStream) -> TokenStream {
7373
7474 let as_debug_all_fields = fields. iter ( ) . map ( |field| {
7575 let name = & field. ident ;
76- let mut type_name = field. ty . to_token_stream ( ) . to_string ( ) ;
77- type_name. retain ( |c| !c. is_whitespace ( ) ) ; // remove whitespace
76+ let type_name = get_type_name ( & field. ty ) ;
7877
7978 // type name or given placeholder string
8079 let placeholder_string = placeholder. as_ref ( ) . unwrap_or ( & type_name) ;
@@ -136,3 +135,72 @@ fn get_placeholder(input: &ItemStruct) -> Result<Option<String>> {
136135 } )
137136 . transpose ( )
138137}
138+
139+ /// returns the type as a string with unnecessary whitespace removed
140+ fn get_type_name ( ty : & Type ) -> String {
141+ let mut type_name = String :: new ( ) ;
142+ let chars: Vec < char > = ty. to_token_stream ( ) . to_string ( ) . trim ( ) . chars ( ) . collect ( ) ;
143+
144+ for ( i, char) in chars. iter ( ) . enumerate ( ) {
145+ if char. is_whitespace ( ) {
146+ // remove whitespace surrounding punctuation
147+ // exceptions are:
148+ // - whitespace surrounding `->`
149+ // - whitespace following `,` or `;`
150+ let ( before, after) = ( chars[ i - 1 ] , chars[ i + 1 ] ) ; // always valid because string was trimmed before
151+ let before_wide = chars. get ( i. saturating_sub ( 2 ) ..i) ;
152+ let after_wide = chars. get ( i + 1 ..=i + 2 ) ;
153+
154+ if ( before. is_ascii_punctuation ( ) || after. is_ascii_punctuation ( ) )
155+ && !matches ! ( before, ';' | ',' )
156+ && !matches ! ( before_wide, Some ( [ '-' , '>' ] ) )
157+ && !matches ! ( after_wide, Some ( [ '-' , '>' ] ) )
158+ {
159+ continue ;
160+ }
161+ }
162+
163+ type_name. push ( * char) ;
164+ }
165+
166+ type_name
167+ }
168+
169+ #[ cfg( test) ]
170+ mod tests {
171+ use super :: * ;
172+
173+ fn test_type_name_formatting ( type_str : & str ) {
174+ let ty: Type = parse_str ( type_str) . unwrap ( ) ;
175+ assert_eq ! ( get_type_name( & ty) , type_str)
176+ }
177+
178+ #[ test]
179+ fn test_no_spaces ( ) {
180+ test_type_name_formatting ( "u8" ) ;
181+ test_type_name_formatting ( "Option<u8>" ) ;
182+ test_type_name_formatting ( "[u8]" ) ;
183+ test_type_name_formatting ( "()" ) ;
184+ test_type_name_formatting ( "std::fmt::Formatter<'_>" ) ;
185+ }
186+ #[ test]
187+ fn test_array ( ) {
188+ test_type_name_formatting ( "[u8; 4]" ) ;
189+ }
190+ #[ test]
191+ fn test_lifetime ( ) {
192+ test_type_name_formatting ( "&'a u8" ) ;
193+ }
194+ #[ test]
195+ fn test_function ( ) {
196+ test_type_name_formatting ( "fn(u8) -> u8" ) ;
197+ }
198+ #[ test]
199+ fn test_trait_object ( ) {
200+ test_type_name_formatting ( "Box<dyn Send>" ) ;
201+ }
202+ #[ test]
203+ fn test_tuple ( ) {
204+ test_type_name_formatting ( "(Option<u8>, u8)" ) ;
205+ }
206+ }
0 commit comments