@@ -51,6 +51,12 @@ impl Field {
5151 None => self . ident . to_string ( ) ,
5252 }
5353 }
54+
55+ // Returns whether this field must be serialized (can't be ignored in case
56+ // that there is no corresponding field in UDT).
57+ fn is_required ( & self ) -> bool {
58+ !self . attrs . skip && !self . attrs . ignore_missing
59+ }
5460}
5561
5662#[ derive( FromAttributes ) ]
@@ -60,6 +66,12 @@ struct FieldAttributes {
6066
6167 #[ darling( default ) ]
6268 skip : bool ,
69+
70+ // If true, then - if this field is missing from the UDT fields metadata
71+ // - it will be ignored during serialization.
72+ #[ darling( default ) ]
73+ #[ darling( rename = "allow_missing" ) ]
74+ ignore_missing : bool ,
6375}
6476
6577struct Context {
@@ -125,6 +137,28 @@ impl Context {
125137 errors. push ( err) ;
126138 }
127139
140+ // When name checks are skipped, fields with `allow_missing` are only
141+ // permitted at the end of the struct, i.e. no field without
142+ // `allow_missing` and `skip` is allowed to be after any field
143+ // with `allow_missing`.
144+ let invalid_default_when_missing_field = self
145+ . fields
146+ . iter ( )
147+ . rev ( )
148+ // Skip the whole suffix of <allow_missing> and <skip>.
149+ . skip_while ( |field| !field. is_required ( ) )
150+ // skip_while finished either because the iterator is empty or it found a field without both <allow_missing> and <skip>.
151+ // In either case, there aren't allowed to be any more fields with `allow_missing`.
152+ . find ( |field| field. attrs . ignore_missing ) ;
153+ if let Some ( invalid) = invalid_default_when_missing_field {
154+ let error =
155+ darling:: Error :: custom (
156+ "when `skip_name_checks` is on, fields with `allow_missing` are only permitted at the end of the struct, \
157+ i.e. no field without `allow_missing` and `skip` is allowed to be after any field with `allow_missing`."
158+ ) . with_span ( & invalid. ident ) ;
159+ errors. push ( error) ;
160+ }
161+
128162 // `rename` annotations don't make sense with skipped name checks
129163 for field in self . fields . iter ( ) {
130164 if field. attrs . rename . is_some ( ) {
@@ -230,6 +264,8 @@ impl<'a> Generator for FieldSortingGenerator<'a> {
230264 . iter ( )
231265 . map ( |f| f. field_name ( ) )
232266 . collect :: < Vec < _ > > ( ) ;
267+ let rust_field_ignore_missing_flags =
268+ self . ctx . fields . iter ( ) . map ( |f| f. attrs . ignore_missing ) ;
233269 let udt_field_names = rust_field_names. clone ( ) ; // For now, it's the same
234270 let field_types = self . ctx . fields . iter ( ) . map ( |f| & f. ty ) . collect :: < Vec < _ > > ( ) ;
235271
@@ -278,15 +314,32 @@ impl<'a> Generator for FieldSortingGenerator<'a> {
278314 . generate_udt_type_match ( parse_quote ! ( #crate_path:: UdtTypeCheckErrorKind :: NotUdt ) ) ,
279315 ) ;
280316
317+ fn make_visited_flag_ident ( field_name : & str ) -> syn:: Ident {
318+ syn:: Ident :: new ( & format ! ( "visited_flag_{}" , field_name) , Span :: call_site ( ) )
319+ }
320+
281321 // Generate a "visited" flag for each field
282322 let visited_flag_names = rust_field_names
283323 . iter ( )
284- . map ( |s| syn :: Ident :: new ( & format ! ( "visited_flag_{}" , s ) , Span :: call_site ( ) ) )
324+ . map ( |s| make_visited_flag_ident ( s ) )
285325 . collect :: < Vec < _ > > ( ) ;
286326 statements. extend :: < Vec < _ > > ( parse_quote ! {
287327 #( let mut #visited_flag_names = false ; ) *
288328 } ) ;
289329
330+ // An iterator over names of Rust fields that can't be ignored
331+ // (i.e., if UDT misses a corresponding field, an error should be raised).
332+ let nonignorable_rust_field_names = self
333+ . ctx
334+ . fields
335+ . iter ( )
336+ . filter ( |f| !f. attrs . ignore_missing )
337+ . map ( |f| f. field_name ( ) ) ;
338+ // An iterator over visited flags of Rust fields that can't be ignored
339+ // (i.e., if UDT misses a corresponding field, an error should be raised).
340+ let nonignorable_visited_flag_names =
341+ nonignorable_rust_field_names. map ( |s| make_visited_flag_ident ( & s) ) ;
342+
290343 // Generate a variable that counts down visited fields.
291344 let field_count = self . ctx . fields . len ( ) ;
292345 statements. push ( parse_quote ! {
@@ -340,19 +393,19 @@ impl<'a> Generator for FieldSortingGenerator<'a> {
340393 } ) ;
341394
342395 // Finally, check that all fields were consumed.
343- // If there are some missing fields, return an error
396+ // If there are some missing fields that don't have the `#[allow_missing]`
397+ // attribute on them, return an error.
344398 statements. push ( parse_quote ! {
345399 if remaining_count > 0 {
346400 #(
347- if !#visited_flag_names {
401+ if !#nonignorable_visited_flag_names && !#rust_field_ignore_missing_flags {
348402 return :: std:: result:: Result :: Err ( mk_typck_err(
349403 #crate_path:: UdtTypeCheckErrorKind :: ValueMissingForUdtField {
350404 field_name: <_ as :: std:: string:: ToString >:: to_string( #rust_field_names) ,
351405 }
352406 ) ) ;
353407 }
354408 ) *
355- :: std:: unreachable!( )
356409 }
357410 } ) ;
358411
@@ -404,25 +457,29 @@ impl<'a> Generator for FieldOrderedGenerator<'a> {
404457 let mut builder = #crate_path:: CellWriter :: into_value_builder( writer) ;
405458 } ) ;
406459
407- // Create an iterator over fields
460+ // Create a peekable iterator over fields.
408461 statements. push ( parse_quote ! {
409- let mut field_iter = field_types. iter( ) ;
462+ let mut field_iter = field_types. iter( ) . peekable ( ) ;
410463 } ) ;
411464
412465 // Serialize each field
413466 for field in self . ctx . fields . iter ( ) {
414467 let rust_field_ident = & field. ident ;
415468 let rust_field_name = field. field_name ( ) ;
469+ let field_can_be_ignored = field. attrs . ignore_missing ;
416470 let typ = & field. ty ;
417471 let name_check_expression: syn:: Expr = if !self . ctx . attributes . skip_name_checks {
418472 parse_quote ! { field_name == #rust_field_name }
419473 } else {
420474 parse_quote ! { true }
421475 } ;
422476 statements. push ( parse_quote ! {
423- match field_iter. next ( ) {
477+ match field_iter. peek ( ) {
424478 Some ( ( field_name, typ) ) => {
425479 if #name_check_expression {
480+ // Advance the iterator.
481+ field_iter. next( ) ;
482+
426483 let sub_builder = #crate_path:: CellValueBuilder :: make_sub_writer( & mut builder) ;
427484 match <#typ as #crate_path:: SerializeValue >:: serialize( & self . #rust_field_ident, typ, sub_builder) {
428485 Ok ( _proof) => { } ,
@@ -435,21 +492,25 @@ impl<'a> Generator for FieldOrderedGenerator<'a> {
435492 ) ) ;
436493 }
437494 }
438- } else {
495+ } else if !#field_can_be_ignored {
439496 return :: std:: result:: Result :: Err ( mk_typck_err(
440497 #crate_path:: UdtTypeCheckErrorKind :: FieldNameMismatch {
441498 rust_field_name: <_ as :: std:: string:: ToString >:: to_string( #rust_field_name) ,
442499 db_field_name: <_ as :: std:: clone:: Clone >:: clone( field_name) . into_owned( ) ,
443500 }
444501 ) ) ;
445502 }
503+ // Else simply ignore the field.
446504 }
447505 None => {
448- return :: std:: result:: Result :: Err ( mk_typck_err(
449- #crate_path:: UdtTypeCheckErrorKind :: ValueMissingForUdtField {
450- field_name: <_ as :: std:: string:: ToString >:: to_string( #rust_field_name) ,
451- }
452- ) ) ;
506+ if !#field_can_be_ignored {
507+ return :: std:: result:: Result :: Err ( mk_typck_err(
508+ #crate_path:: UdtTypeCheckErrorKind :: ValueMissingForUdtField {
509+ field_name: <_ as :: std:: string:: ToString >:: to_string( #rust_field_name) ,
510+ }
511+ ) ) ;
512+ }
513+ // Else the field is ignored and we continue with other fields.
453514 }
454515 }
455516 } ) ;
0 commit comments