@@ -6,17 +6,15 @@ use std::{
6
6
borrow:: Cow ,
7
7
env,
8
8
fmt:: Display ,
9
- io:: { self , Write as _ } ,
9
+ io:: { self , Write } ,
10
10
} ;
11
- use syn:: { DataEnum , DataStruct , DataUnion , DeriveInput , Lit } ;
12
-
13
- const DISABLE_WARNINGS_VAR : & str = "SHUT_UP_CW_SCHEMA_DERIVE" ;
14
-
15
- fn print_warning ( title : impl Display , content : impl Display ) -> io:: Result < ( ) > {
16
- if let Ok ( "1" ) = env:: var ( DISABLE_WARNINGS_VAR ) . as_deref ( ) {
17
- return Ok ( ( ) ) ;
18
- }
11
+ use syn:: { spanned:: Spanned , DataEnum , DataStruct , DataUnion , DeriveInput , Lit } ;
19
12
13
+ fn print_warning (
14
+ span : proc_macro2:: Span ,
15
+ title : impl Display ,
16
+ content : impl Display ,
17
+ ) -> io:: Result < ( ) > {
20
18
let mut sink = io:: stderr ( ) ;
21
19
22
20
let bold_yellow = Style :: new ( ) . bold ( ) . yellow ( ) ;
@@ -34,14 +32,17 @@ fn print_warning(title: impl Display, content: impl Display) -> io::Result<()> {
34
32
write ! ( sink, "{}" , " | " . style( blue) ) ?;
35
33
writeln ! ( sink, "{content}" ) ?;
36
34
37
- writeln ! ( sink, "{}" , " | " . style( blue) ) ?;
35
+ let span = span. start ( ) ;
36
+ write ! ( sink, "{}" , " | " . style( blue) ) ?;
37
+ writeln ! ( sink, "Location: {}:{}" , span. line, span. column) ?;
38
+
38
39
writeln ! ( sink, "{}" , " | " . style( blue) ) ?;
39
40
40
41
write ! ( sink, "{}" , " = " . style( blue) ) ?;
41
42
write ! ( sink, "{}" , "note: " . style( bold) ) ?;
42
43
writeln ! (
43
44
sink,
44
- "set `{DISABLE_WARNINGS_VAR}=1` to silence this warning "
45
+ "annotate the container with #[schemaifier(mute_warnings)] to disable these warnings "
45
46
) ?;
46
47
47
48
Ok ( ( ) )
@@ -90,7 +91,7 @@ struct SerdeContainerOptions {
90
91
}
91
92
92
93
impl SerdeContainerOptions {
93
- fn parse ( attributes : & [ syn:: Attribute ] ) -> syn:: Result < Self > {
94
+ fn parse ( attributes : & [ syn:: Attribute ] , muted_warnings : bool ) -> syn:: Result < Self > {
94
95
let mut options = SerdeContainerOptions {
95
96
rename_all : None ,
96
97
untagged : false ,
@@ -106,7 +107,8 @@ impl SerdeContainerOptions {
106
107
} else if meta. path . is_ident ( "untagged" ) {
107
108
options. untagged = true ;
108
109
} else {
109
- /*print_warning(
110
+ print_warning (
111
+ meta. path . span ( ) ,
110
112
"unknown serde attribute" ,
111
113
format ! (
112
114
"unknown attribute \" {}\" " ,
@@ -116,7 +118,7 @@ impl SerdeContainerOptions {
116
118
. unwrap_or_else( || "[error]" . into( ) )
117
119
) ,
118
120
)
119
- .unwrap();*/
121
+ . unwrap ( ) ;
120
122
121
123
// TODO: support other serde attributes
122
124
//
@@ -127,8 +129,10 @@ impl SerdeContainerOptions {
127
129
. unwrap_or_else ( |_| meta. input . cursor ( ) . token_stream ( ) ) ;
128
130
}
129
131
130
- if meta. path . is_ident ( "untagged" ) || meta. path . is_ident ( "tag" ) {
132
+ if ( meta. path . is_ident ( "untagged" ) || meta. path . is_ident ( "tag" ) ) && !muted_warnings
133
+ {
131
134
print_warning (
135
+ meta. path . span ( ) ,
132
136
"unsupported tag type" ,
133
137
meta. error ( "unsupported tag type" ) . to_string ( ) ,
134
138
)
@@ -147,6 +151,7 @@ struct ContainerOptions {
147
151
r#as : Option < syn:: Ident > ,
148
152
r#type : Option < syn:: Expr > ,
149
153
crate_path : syn:: Path ,
154
+ muted_warnings : bool ,
150
155
}
151
156
152
157
impl ContainerOptions {
@@ -155,6 +160,7 @@ impl ContainerOptions {
155
160
r#as : None ,
156
161
r#type : None ,
157
162
crate_path : syn:: parse_str ( "::cw_schema" ) ?,
163
+ muted_warnings : false ,
158
164
} ;
159
165
160
166
for attribute in attributes
@@ -169,6 +175,8 @@ impl ContainerOptions {
169
175
options. r#as = Some ( meta. value ( ) ?. parse ( ) ?) ;
170
176
} else if meta. path . is_ident ( "type" ) {
171
177
options. r#type = Some ( meta. value ( ) ?. parse ( ) ?) ;
178
+ } else if meta. path . is_ident ( "mute_warnings" ) {
179
+ options. muted_warnings = true ;
172
180
} else {
173
181
bail ! ( meta. path, "unknown attribute" ) ;
174
182
}
@@ -182,12 +190,18 @@ impl ContainerOptions {
182
190
}
183
191
184
192
struct SerdeFieldOptions {
193
+ default : bool ,
185
194
rename : Option < syn:: LitStr > ,
195
+ skip_serializing_if : Option < syn:: Expr > ,
186
196
}
187
197
188
198
impl SerdeFieldOptions {
189
- fn parse ( attributes : & [ syn:: Attribute ] ) -> syn:: Result < Self > {
190
- let mut options = SerdeFieldOptions { rename : None } ;
199
+ fn parse ( attributes : & [ syn:: Attribute ] , muted_warnings : bool ) -> syn:: Result < Self > {
200
+ let mut options = SerdeFieldOptions {
201
+ default : false ,
202
+ rename : None ,
203
+ skip_serializing_if : None ,
204
+ } ;
191
205
192
206
for attribute in attributes
193
207
. iter ( )
@@ -196,18 +210,28 @@ impl SerdeFieldOptions {
196
210
attribute. parse_nested_meta ( |meta| {
197
211
if meta. path . is_ident ( "rename" ) {
198
212
options. rename = Some ( meta. value ( ) ?. parse ( ) ?) ;
213
+ } else if meta. path . is_ident ( "default" ) {
214
+ options. default = true ;
215
+ // just ignore the rest. it's not relevant.
216
+ // but without this code, we'd sometimes hit compile errors.
217
+ let _ = meta. value ( ) . and_then ( |val| val. parse :: < syn:: Expr > ( ) ) ;
218
+ } else if meta. path . is_ident ( "skip_serializing_if" ) {
219
+ options. skip_serializing_if = Some ( meta. value ( ) ?. parse ( ) ?) ;
199
220
} else {
200
- print_warning (
201
- "unknown serde attribute" ,
202
- format ! (
203
- "unknown attribute \" {}\" " ,
204
- meta. path
205
- . get_ident( )
206
- . map( |ident| ident. to_string( ) )
207
- . unwrap_or_else( || "[error]" . into( ) )
208
- ) ,
209
- )
210
- . unwrap ( ) ;
221
+ if !muted_warnings {
222
+ print_warning (
223
+ meta. path . span ( ) ,
224
+ "unknown serde attribute" ,
225
+ format ! (
226
+ "unknown attribute \" {}\" " ,
227
+ meta. path
228
+ . get_ident( )
229
+ . map( |ident| ident. to_string( ) )
230
+ . unwrap_or_else( || "[error]" . into( ) )
231
+ ) ,
232
+ )
233
+ . unwrap ( ) ;
234
+ }
211
235
212
236
// TODO: support other serde attributes
213
237
//
@@ -287,19 +311,25 @@ fn collect_struct_fields<'a, C>(
287
311
converter : & ' a C ,
288
312
crate_path : & ' a syn:: Path ,
289
313
fields : & ' a syn:: FieldsNamed ,
314
+ muted_warnings : bool ,
290
315
) -> impl Iterator < Item = syn:: Result < TokenStream > > + ' a
291
316
where
292
317
C : Fn ( & syn:: Ident ) -> syn:: Ident ,
293
318
{
294
319
fields. named . iter ( ) . map ( move |field| {
295
- let field_options = SerdeFieldOptions :: parse ( & field. attrs ) ?;
320
+ let field_options = SerdeFieldOptions :: parse ( & field. attrs , muted_warnings ) ?;
296
321
297
322
let name = field_options
298
323
. rename
299
324
. map ( |lit_str| format_ident ! ( "{}" , lit_str. value( ) ) )
300
325
. unwrap_or_else ( || converter ( field. ident . as_ref ( ) . unwrap ( ) ) ) ;
301
326
let description = normalize_option ( extract_documentation ( & field. attrs ) ?) ;
302
- let field_ty = & field. ty ;
327
+ let field_ty = if field_options. default || field_options. skip_serializing_if . is_some ( ) {
328
+ let ty = & field. ty ;
329
+ syn:: parse_quote!( :: core:: option:: Option <#ty>)
330
+ } else {
331
+ field. ty . clone ( )
332
+ } ;
303
333
304
334
let expanded = quote ! {
305
335
(
@@ -325,8 +355,13 @@ fn expand_enum(mut meta: ContainerMeta, input: DataEnum) -> syn::Result<TokenStr
325
355
for variant in input. variants . iter ( ) {
326
356
let value = match variant. fields {
327
357
syn:: Fields :: Named ( ref fields) => {
328
- let items = collect_struct_fields ( & converter, crate_path, fields)
329
- . collect :: < syn:: Result < Vec < _ > > > ( ) ?;
358
+ let items = collect_struct_fields (
359
+ & converter,
360
+ crate_path,
361
+ fields,
362
+ meta. options . muted_warnings ,
363
+ )
364
+ . collect :: < syn:: Result < Vec < _ > > > ( ) ?;
330
365
331
366
quote ! {
332
367
#crate_path:: EnumValue :: Named {
@@ -350,7 +385,7 @@ fn expand_enum(mut meta: ContainerMeta, input: DataEnum) -> syn::Result<TokenStr
350
385
syn:: Fields :: Unit => quote ! { #crate_path:: EnumValue :: Unit } ,
351
386
} ;
352
387
353
- let field_options = SerdeFieldOptions :: parse ( & variant. attrs ) ?;
388
+ let field_options = SerdeFieldOptions :: parse ( & variant. attrs , meta . options . muted_warnings ) ?;
354
389
355
390
let variant_name = field_options
356
391
. rename
@@ -432,8 +467,13 @@ fn expand_struct(mut meta: ContainerMeta, input: DataStruct) -> syn::Result<Toke
432
467
} else {
433
468
let node_ty = match input. fields {
434
469
syn:: Fields :: Named ( ref named) => {
435
- let items = collect_struct_fields ( & converter, crate_path, named)
436
- . collect :: < syn:: Result < Vec < _ > > > ( ) ?;
470
+ let items = collect_struct_fields (
471
+ & converter,
472
+ crate_path,
473
+ named,
474
+ meta. options . muted_warnings ,
475
+ )
476
+ . collect :: < syn:: Result < Vec < _ > > > ( ) ?;
437
477
438
478
quote ! {
439
479
#crate_path:: StructType :: Named {
@@ -508,7 +548,7 @@ fn expand_union(_meta: ContainerMeta, input: DataUnion) -> syn::Result<TokenStre
508
548
509
549
pub fn expand ( input : DeriveInput ) -> syn:: Result < TokenStream > {
510
550
let options = ContainerOptions :: parse ( & input. attrs ) ?;
511
- let serde_options = SerdeContainerOptions :: parse ( & input. attrs ) ?;
551
+ let serde_options = SerdeContainerOptions :: parse ( & input. attrs , options . muted_warnings ) ?;
512
552
let description = extract_documentation ( & input. attrs ) ?;
513
553
514
554
let meta = ContainerMeta {
0 commit comments