@@ -2,7 +2,9 @@ use proc_macro2::{Ident, TokenStream};
2
2
use quote:: quote;
3
3
use syn:: { punctuated:: Punctuated , Data , DeriveInput , Fields , LitStr , Token } ;
4
4
5
- use crate :: helpers:: { non_enum_error, HasStrumVariantProperties , HasTypeProperties } ;
5
+ use crate :: helpers:: {
6
+ non_enum_error, non_single_field_variant_error, HasStrumVariantProperties , HasTypeProperties ,
7
+ } ;
6
8
7
9
pub fn display_inner ( ast : & DeriveInput ) -> syn:: Result < TokenStream > {
8
10
let name = & ast. ident ;
@@ -23,6 +25,16 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
23
25
continue ;
24
26
}
25
27
28
+ if let Some ( ..) = variant_properties. transparent {
29
+ let arm = super :: extract_single_field_variant_and_then ( name, variant, |tok| {
30
+ quote ! { :: core:: fmt:: Display :: fmt( #tok, f) }
31
+ } )
32
+ . map_err ( |_| non_single_field_variant_error ( "transparent" ) ) ?;
33
+
34
+ arms. push ( arm) ;
35
+ continue ;
36
+ }
37
+
26
38
// Look at all the serialize attributes.
27
39
let output = variant_properties
28
40
. get_preferred_name ( type_properties. case_style , type_properties. prefix . as_ref ( ) ) ;
@@ -37,7 +49,8 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
37
49
. enumerate ( )
38
50
. map ( |( index, field) | {
39
51
assert ! ( field. ident. is_none( ) ) ;
40
- let ident = syn:: parse_str :: < Ident > ( format ! ( "field{}" , index) . as_str ( ) ) . unwrap ( ) ;
52
+ let ident =
53
+ syn:: parse_str :: < Ident > ( format ! ( "field{}" , index) . as_str ( ) ) . unwrap ( ) ;
41
54
quote ! { ref #ident }
42
55
} )
43
56
. collect ( ) ;
@@ -59,86 +72,87 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
59
72
} ;
60
73
61
74
if variant_properties. to_string . is_none ( ) && variant_properties. default . is_some ( ) {
62
- match & variant. fields {
63
- Fields :: Unnamed ( fields) if fields. unnamed . len ( ) == 1 => {
64
- arms. push ( quote ! { #name:: #ident( ref s) => :: core:: fmt:: Display :: fmt( s, f) } ) ;
75
+ let arm = super :: extract_single_field_variant_and_then ( name, variant, |tok| {
76
+ quote ! { :: core:: fmt:: Display :: fmt( #tok, f) }
77
+ } )
78
+ . map_err ( |_| {
79
+ syn:: Error :: new_spanned (
80
+ variant,
81
+ "Default only works on newtype structs with a single String field" ,
82
+ )
83
+ } ) ?;
84
+
85
+ arms. push ( arm) ;
86
+ continue ;
87
+ }
88
+
89
+ let arm = match variant. fields {
90
+ Fields :: Named ( ref field_names) => {
91
+ let used_vars = capture_format_string_idents ( & output) ?;
92
+ if used_vars. is_empty ( ) {
93
+ quote ! { #name:: #ident #params => :: core:: fmt:: Display :: fmt( #output, f) }
94
+ } else {
95
+ // Create args like 'name = name, age = age' for format macro
96
+ let args: Punctuated < _ , Token ! ( , ) > = field_names
97
+ . named
98
+ . iter ( )
99
+ . filter_map ( |field| {
100
+ let ident = field. ident . as_ref ( ) . unwrap ( ) ;
101
+ // Only contain variables that are used in format string
102
+ if !used_vars. contains ( ident) {
103
+ None
104
+ } else {
105
+ Some ( quote ! { #ident = #ident } )
106
+ }
107
+ } )
108
+ . collect ( ) ;
109
+
110
+ quote ! {
111
+ #[ allow( unused_variables) ]
112
+ #name:: #ident #params => :: core:: fmt:: Display :: fmt( & format_args!( #output, #args) , f)
113
+ }
65
114
}
66
- _ => {
115
+ }
116
+ Fields :: Unnamed ( ref unnamed_fields) => {
117
+ let used_vars = capture_format_strings ( & output) ?;
118
+ if used_vars. iter ( ) . any ( String :: is_empty) {
67
119
return Err ( syn:: Error :: new_spanned (
68
- variant ,
69
- "Default only works on newtype structs with a single String field " ,
70
- ) )
120
+ & output ,
121
+ "Empty {} is not allowed; Use manual numbering ({0}) " ,
122
+ ) ) ;
71
123
}
72
- }
73
- } else {
74
- let arm = match variant. fields {
75
- Fields :: Named ( ref field_names) => {
76
- let used_vars = capture_format_string_idents ( & output) ?;
77
- if used_vars. is_empty ( ) {
78
- quote ! { #name:: #ident #params => :: core:: fmt:: Display :: fmt( #output, f) }
79
- } else {
80
- // Create args like 'name = name, age = age' for format macro
81
- let args: Punctuated < _ , Token ! ( , ) > = field_names
82
- . named
83
- . iter ( )
84
- . filter_map ( |field| {
85
- let ident = field. ident . as_ref ( ) . unwrap ( ) ;
86
- // Only contain variables that are used in format string
87
- if !used_vars. contains ( ident) {
88
- None
89
- } else {
90
- Some ( quote ! { #ident = #ident } )
91
- }
92
- } )
93
- . collect ( ) ;
94
-
95
- quote ! {
96
- #[ allow( unused_variables) ]
97
- #name:: #ident #params => :: core:: fmt:: Display :: fmt( & format_args!( #output, #args) , f)
98
- }
99
- }
100
- } ,
101
- Fields :: Unnamed ( ref unnamed_fields) => {
102
- let used_vars = capture_format_strings ( & output) ?;
103
- if used_vars. iter ( ) . any ( String :: is_empty) {
104
- return Err ( syn:: Error :: new_spanned (
105
- & output,
106
- "Empty {} is not allowed; Use manual numbering ({0})" ,
107
- ) )
108
- }
109
- if used_vars. is_empty ( ) {
110
- quote ! { #name:: #ident #params => :: core:: fmt:: Display :: fmt( #output, f) }
111
- } else {
112
- let args: Punctuated < _ , Token ! ( , ) > = unnamed_fields
113
- . unnamed
114
- . iter ( )
115
- . enumerate ( )
116
- . map ( |( index, field) | {
117
- assert ! ( field. ident. is_none( ) ) ;
118
- syn:: parse_str :: < Ident > ( format ! ( "field{}" , index) . as_str ( ) ) . unwrap ( )
119
- } )
120
- . collect ( ) ;
121
- quote ! {
122
- #[ allow( unused_variables) ]
123
- #name:: #ident #params => :: core:: fmt:: Display :: fmt( & format!( #output, #args) , f)
124
- }
124
+ if used_vars. is_empty ( ) {
125
+ quote ! { #name:: #ident #params => :: core:: fmt:: Display :: fmt( #output, f) }
126
+ } else {
127
+ let args: Punctuated < _ , Token ! ( , ) > = unnamed_fields
128
+ . unnamed
129
+ . iter ( )
130
+ . enumerate ( )
131
+ . map ( |( index, field) | {
132
+ assert ! ( field. ident. is_none( ) ) ;
133
+ syn:: parse_str :: < Ident > ( format ! ( "field{}" , index) . as_str ( ) ) . unwrap ( )
134
+ } )
135
+ . collect ( ) ;
136
+ quote ! {
137
+ #[ allow( unused_variables) ]
138
+ #name:: #ident #params => :: core:: fmt:: Display :: fmt( & format!( #output, #args) , f)
125
139
}
126
140
}
127
- Fields :: Unit => {
128
- let used_vars = capture_format_strings ( & output) ?;
129
- if !used_vars. is_empty ( ) {
130
- return Err ( syn:: Error :: new_spanned (
131
- & output,
132
- "Unit variants do not support interpolation" ,
133
- ) ) ;
134
- }
135
-
136
- quote ! { #name:: #ident #params => :: core:: fmt:: Display :: fmt( #output, f) }
141
+ }
142
+ Fields :: Unit => {
143
+ let used_vars = capture_format_strings ( & output) ?;
144
+ if !used_vars. is_empty ( ) {
145
+ return Err ( syn:: Error :: new_spanned (
146
+ & output,
147
+ "Unit variants do not support interpolation" ,
148
+ ) ) ;
137
149
}
138
- } ;
139
150
140
- arms. push ( arm) ;
141
- }
151
+ quote ! { #name:: #ident #params => :: core:: fmt:: Display :: fmt( #output, f) }
152
+ }
153
+ } ;
154
+
155
+ arms. push ( arm) ;
142
156
}
143
157
144
158
if arms. len ( ) < variants. len ( ) {
@@ -157,14 +171,17 @@ pub fn display_inner(ast: &DeriveInput) -> syn::Result<TokenStream> {
157
171
}
158
172
159
173
fn capture_format_string_idents ( string_literal : & LitStr ) -> syn:: Result < Vec < Ident > > {
160
- capture_format_strings ( string_literal) ?. into_iter ( ) . map ( |ident| {
161
- syn:: parse_str :: < Ident > ( ident. as_str ( ) ) . map_err ( |_| {
162
- syn:: Error :: new_spanned (
163
- string_literal,
164
- "Invalid identifier inside format string bracket" ,
165
- )
174
+ capture_format_strings ( string_literal) ?
175
+ . into_iter ( )
176
+ . map ( |ident| {
177
+ syn:: parse_str :: < Ident > ( ident. as_str ( ) ) . map_err ( |_| {
178
+ syn:: Error :: new_spanned (
179
+ string_literal,
180
+ "Invalid identifier inside format string bracket" ,
181
+ )
182
+ } )
166
183
} )
167
- } ) . collect ( )
184
+ . collect ( )
168
185
}
169
186
170
187
fn capture_format_strings ( string_literal : & LitStr ) -> syn:: Result < Vec < String > > {
0 commit comments