@@ -13,6 +13,7 @@ use quote::quote;
13
13
use quote:: quote_spanned;
14
14
use std:: borrow:: Cow ;
15
15
use std:: collections:: VecDeque ;
16
+ use std:: iter:: once;
16
17
use syn:: parse_quote;
17
18
use syn:: parse_quote_spanned;
18
19
use syn:: spanned:: Spanned ;
@@ -574,10 +575,12 @@ fn generate_field_accessor<'a>(
574
575
FieldOrGroup :: Field ( _) => quote ! ( #ident) ,
575
576
FieldOrGroup :: Group ( _) => quote ! ( #ident) ,
576
577
FieldOrGroup :: Multi ( _) if exclude_parents => {
578
+ // Interacting with TEdgeConfigReader - parents already included in value
577
579
let field = id_gen. next_id ( ident. span ( ) ) ;
578
580
quote_spanned ! ( ident. span( ) => #ident. #method( #field. as_deref( ) ) ?)
579
581
}
580
582
FieldOrGroup :: Multi ( _) => {
583
+ // Interacting with TEdgeConfigDto - parents need to be supplied with try_get_mut
581
584
let field = id_gen. next_id ( ident. span ( ) ) ;
582
585
#[ allow( unstable_name_collisions) ]
583
586
let parents = fields_so_far
@@ -591,6 +594,38 @@ fn generate_field_accessor<'a>(
591
594
} )
592
595
}
593
596
597
+ fn generate_multi_dto_cleanup ( fields : & VecDeque < & FieldOrGroup > ) -> Vec < syn:: Stmt > {
598
+ let mut id_gen = SequentialIdGenerator :: default ( ) ;
599
+ let mut all_idents = Vec :: new ( ) ;
600
+ let mut fields_so_far = Vec :: new ( ) ;
601
+ let mut result = Vec :: new ( ) ;
602
+ for field in fields {
603
+ let ident = field. ident ( ) ;
604
+ all_idents. push ( ident) ;
605
+ match field {
606
+ FieldOrGroup :: Multi ( _) => {
607
+ let field = id_gen. next_id ( ident. span ( ) ) ;
608
+ #[ allow( unstable_name_collisions) ]
609
+ let parents = all_idents
610
+ . iter ( )
611
+ . map ( |id| id. to_string ( ) )
612
+ . intersperse ( "." . to_owned ( ) )
613
+ . collect :: < String > ( ) ;
614
+ result. push ( fields_so_far. iter ( ) . cloned ( ) . chain ( once ( quote_spanned ! ( ident. span( ) => #ident. remove_if_empty( #field. as_deref( ) ) ) ) ) . collect :: < Vec < _ > > ( ) ) ;
615
+ fields_so_far. push (
616
+ quote_spanned ! ( ident. span( ) => #ident. try_get_mut( #field. as_deref( ) , #parents) ?) ,
617
+ ) ;
618
+ }
619
+ _ => fields_so_far. push ( quote ! ( #ident) ) ,
620
+ }
621
+ }
622
+ result
623
+ . into_iter ( )
624
+ . rev ( )
625
+ . map ( |fields| parse_quote ! ( self . #( #fields) . * ; ) )
626
+ . collect ( )
627
+ }
628
+
594
629
fn generate_string_readers ( paths : & [ VecDeque < & FieldOrGroup > ] ) -> TokenStream {
595
630
let enum_variants = paths. iter ( ) . map ( enum_variant) ;
596
631
let arms = paths
@@ -655,6 +690,7 @@ fn generate_string_writers(paths: &[VecDeque<&FieldOrGroup>]) -> TokenStream {
655
690
. map ( |( path, configuration_key) | {
656
691
let read_segments = generate_field_accessor ( path, "try_get" , true ) ;
657
692
let write_segments = generate_field_accessor ( path, "try_get_mut" , false ) . collect :: < Vec < _ > > ( ) ;
693
+ let cleanup_stmts = generate_multi_dto_cleanup ( path) ;
658
694
let field = path
659
695
. iter ( )
660
696
. filter_map ( |thing| thing. field ( ) )
@@ -687,7 +723,10 @@ fn generate_string_writers(paths: &[VecDeque<&FieldOrGroup>]) -> TokenStream {
687
723
. map_err( |e| WriteError :: ParseValue ( Box :: new( e) ) ) ?) ,
688
724
} ,
689
725
parse_quote_spanned ! { ty. span( ) =>
690
- WritableKey :: #match_variant => self . #( #write_segments) . * = None ,
726
+ WritableKey :: #match_variant => {
727
+ self . #( #write_segments) . * = None ;
728
+ #( #cleanup_stmts) *
729
+ } ,
691
730
} ,
692
731
parse_quote_spanned ! { ty. span( ) =>
693
732
WritableKey :: #match_variant => self . #( #write_segments) . * = <#ty as AppendRemoveItem >:: append(
@@ -1390,6 +1429,57 @@ mod tests {
1390
1429
) ;
1391
1430
}
1392
1431
1432
+ #[ test]
1433
+ fn impl_try_unset_key_calls_multi_dto_cleanup ( ) {
1434
+ let input: crate :: input:: Configuration = parse_quote ! (
1435
+ #[ tedge_config( multi) ]
1436
+ c8y: {
1437
+ url: String ,
1438
+
1439
+ #[ tedge_config( multi) ]
1440
+ nested: {
1441
+ field: bool ,
1442
+ }
1443
+ } ,
1444
+
1445
+ sudo: {
1446
+ enable: bool ,
1447
+ } ,
1448
+ ) ;
1449
+ let paths = configuration_paths_from ( & input. groups ) ;
1450
+ let writers = generate_string_writers ( & paths) ;
1451
+ let impl_dto_block = syn:: parse2 ( writers) . unwrap ( ) ;
1452
+ let impl_dto_block = retain_fn ( impl_dto_block, "try_unset_key" ) ;
1453
+
1454
+ let expected = parse_quote ! {
1455
+ impl TEdgeConfigDto {
1456
+ pub fn try_unset_key( & mut self , key: & WritableKey ) -> Result <( ) , WriteError > {
1457
+ match key {
1458
+ WritableKey :: C8yUrl ( key0) => {
1459
+ self . c8y. try_get_mut( key0. as_deref( ) , "c8y" ) ?. url = None ;
1460
+ self . c8y. remove_if_empty( key0. as_deref( ) ) ;
1461
+ }
1462
+ WritableKey :: C8yNestedField ( key0, key1) => {
1463
+ self . c8y. try_get_mut( key0. as_deref( ) , "c8y" ) ?. nested. try_get_mut( key1. as_deref( ) , "c8y.nested" ) ?. field = None ;
1464
+ // The fields should be removed from deepest to shallowest
1465
+ self . c8y. try_get_mut( key0. as_deref( ) , "c8y" ) ?. nested. remove_if_empty( key1. as_deref( ) ) ;
1466
+ self . c8y. remove_if_empty( key0. as_deref( ) ) ;
1467
+ }
1468
+ WritableKey :: SudoEnable => {
1469
+ self . sudo. enable = None ;
1470
+ } ,
1471
+ } ;
1472
+ Ok ( ( ) )
1473
+ }
1474
+ }
1475
+ } ;
1476
+
1477
+ pretty_assertions:: assert_eq!(
1478
+ prettyplease:: unparse( & parse_quote!( #impl_dto_block) ) ,
1479
+ prettyplease:: unparse( & expected)
1480
+ )
1481
+ }
1482
+
1393
1483
fn keys_enum_impl_block ( config_keys : & ( Vec < String > , Vec < ConfigurationKey > ) ) -> ItemImpl {
1394
1484
let generated = keys_enum ( parse_quote ! ( ReadableKey ) , config_keys, "DOC FRAGMENT" ) ;
1395
1485
let generated_file: syn:: File = syn:: parse2 ( generated) . unwrap ( ) ;
0 commit comments