@@ -8,11 +8,13 @@ use crate::dscresources::resource_manifest::{import_manifest, validate_semver, K
8
8
use crate :: dscresources:: command_resource:: invoke_command;
9
9
use crate :: dscerror:: DscError ;
10
10
use indicatif:: ProgressStyle ;
11
+ use linked_hash_map:: LinkedHashMap ;
11
12
use regex:: RegexBuilder ;
12
13
use semver:: Version ;
13
- use std:: collections:: { BTreeMap , HashSet } ;
14
+ use std:: collections:: { BTreeMap , HashSet , HashMap } ;
14
15
use std:: env;
15
16
use std:: ffi:: OsStr ;
17
+ use std:: fs;
16
18
use std:: fs:: File ;
17
19
use std:: io:: BufReader ;
18
20
use std:: path:: { Path , PathBuf } ;
@@ -279,6 +281,7 @@ impl ResourceDiscovery for CommandDiscovery {
279
281
}
280
282
281
283
self . adapted_resources = adapted_resources;
284
+
282
285
Ok ( ( ) )
283
286
}
284
287
@@ -294,6 +297,11 @@ impl ResourceDiscovery for CommandDiscovery {
294
297
} else {
295
298
self . discover_resources ( "*" ) ?;
296
299
self . discover_adapted_resources ( type_name_filter, adapter_name_filter) ?;
300
+
301
+ // add/update found adapted resources to the lookup_table
302
+ add_resources_to_lookup_table ( & self . adapted_resources ) ;
303
+
304
+ // note: in next line 'BTreeMap::append' will leave self.adapted_resources empty
297
305
resources. append ( & mut self . adapted_resources ) ;
298
306
}
299
307
@@ -334,7 +342,8 @@ impl ResourceDiscovery for CommandDiscovery {
334
342
debug ! ( "Found {} matching non-adapter-based resources" , found_resources. len( ) ) ;
335
343
336
344
// now go through the adapters
337
- for ( adapter_name, adapters) in self . adapters . clone ( ) {
345
+ let sorted_adapters = sort_adapters_based_on_lookup_table ( & self . adapters , & remaining_required_resource_types) ;
346
+ for ( adapter_name, adapters) in sorted_adapters {
338
347
// TODO: handle version requirements
339
348
let Some ( adapter) = adapters. first ( ) else {
340
349
// skip if no adapters
@@ -353,6 +362,8 @@ impl ResourceDiscovery for CommandDiscovery {
353
362
}
354
363
355
364
self . discover_adapted_resources ( "*" , & adapter_name) ?;
365
+ // add/update found adapted resources to the lookup_table
366
+ add_resources_to_lookup_table ( & self . adapted_resources ) ;
356
367
357
368
// now go through the adapter resources and add them to the list of resources
358
369
for ( adapted_name, adapted_resource) in & self . adapted_resources {
@@ -496,3 +507,95 @@ fn load_manifest(path: &Path) -> Result<DscResource, DscError> {
496
507
497
508
Ok ( resource)
498
509
}
510
+
511
+ fn sort_adapters_based_on_lookup_table ( unsorted_adapters : & BTreeMap < String , Vec < DscResource > > , needed_resource_types : & Vec < String > ) -> LinkedHashMap < String , Vec < DscResource > >
512
+ {
513
+ let mut result = LinkedHashMap :: < String , Vec < DscResource > > :: new ( ) ;
514
+ let lookup_table = load_adapted_resources_lookup_table ( ) ;
515
+ // first add adapters (for needed types) that can be found in the lookup table
516
+ for needed_resource in needed_resource_types {
517
+ if let Some ( adapter_name) = lookup_table. get ( needed_resource) {
518
+ if let Some ( resource_vec) = unsorted_adapters. get ( adapter_name) {
519
+ debug ! ( "Lookup table found resource '{}' in adapter '{}'" , needed_resource, adapter_name) ;
520
+ result. insert ( adapter_name. to_string ( ) , resource_vec. clone ( ) ) ;
521
+ }
522
+ }
523
+ }
524
+
525
+ // now add remaining adapters
526
+ for ( adapter_name, adapters) in unsorted_adapters {
527
+ if !result. contains_key ( adapter_name) {
528
+ result. insert ( adapter_name. to_string ( ) , adapters. clone ( ) ) ;
529
+ }
530
+ }
531
+
532
+ result
533
+ }
534
+
535
+ fn add_resources_to_lookup_table ( adapted_resources : & BTreeMap < String , Vec < DscResource > > )
536
+ {
537
+ let mut lookup_table = load_adapted_resources_lookup_table ( ) ;
538
+
539
+ for ( resource_name, res_vec) in adapted_resources {
540
+ if let Some ( adapter_name) = & res_vec[ 0 ] . require_adapter {
541
+ lookup_table. insert ( resource_name. to_string ( ) . to_lowercase ( ) , adapter_name. to_string ( ) ) ;
542
+ } else {
543
+ info ! ( "Resource '{resource_name}' in 'adapted_resources' is missing 'require_adapter' field." ) ;
544
+ }
545
+ } ;
546
+
547
+ save_adapted_resources_lookup_table ( & lookup_table) ;
548
+ }
549
+
550
+ fn save_adapted_resources_lookup_table ( lookup_table : & HashMap < String , String > )
551
+ {
552
+ if let Ok ( lookup_table_json) = serde_json:: to_string ( & lookup_table) {
553
+ let file_path = get_lookup_table_file_path ( ) ;
554
+ debug ! ( "Saving lookup table with {} items to {:?}" , lookup_table. len( ) , file_path) ;
555
+
556
+ let path = std:: path:: Path :: new ( & file_path) ;
557
+ if let Some ( prefix) = path. parent ( ) {
558
+ if fs:: create_dir_all ( prefix) . is_ok ( ) {
559
+ if fs:: write ( file_path. clone ( ) , lookup_table_json) . is_err ( ) {
560
+ info ! ( "Unable to write lookup_table file {file_path:?}" ) ;
561
+ }
562
+ } else {
563
+ info ! ( "Unable to create parent directories of the lookup_table file {file_path:?}" ) ;
564
+ }
565
+ } else {
566
+ info ! ( "Unable to get directory of the lookup_table file {file_path:?}" ) ;
567
+ }
568
+ } else {
569
+ info ! ( "Unable to serialize lookup_table to json" ) ;
570
+ }
571
+ }
572
+
573
+ fn load_adapted_resources_lookup_table ( ) -> HashMap < String , String >
574
+ {
575
+ let file_path = get_lookup_table_file_path ( ) ;
576
+
577
+ let lookup_table: HashMap < String , String > = match fs:: read ( file_path. clone ( ) ) {
578
+ Ok ( data) => { serde_json:: from_slice ( & data) . unwrap_or_default ( ) } ,
579
+ Err ( _) => { HashMap :: new ( ) }
580
+ } ;
581
+
582
+ debug ! ( "Read {} items into lookup table from {:?}" , lookup_table. len( ) , file_path) ;
583
+ lookup_table
584
+ }
585
+
586
+ #[ cfg( target_os = "windows" ) ]
587
+ fn get_lookup_table_file_path ( ) -> String
588
+ {
589
+ // $env:LocalAppData+"dsc\AdaptedResourcesLookupTable.json"
590
+ let Ok ( local_app_data_path) = std:: env:: var ( "LocalAppData" ) else { return String :: new ( ) ; } ;
591
+
592
+ Path :: new ( & local_app_data_path) . join ( "dsc" ) . join ( "AdaptedResourcesLookupTable.json" ) . display ( ) . to_string ( )
593
+ }
594
+
595
+ #[ cfg( not( target_os = "windows" ) ) ]
596
+ fn get_lookup_table_file_path ( ) -> String
597
+ {
598
+ // $env:HOME+".dsc/AdaptedResourcesLookupTable.json"
599
+ let Ok ( home_path) = std:: env:: var ( "HOME" ) else { return String :: new ( ) ; } ;
600
+ Path :: new ( & home_path) . join ( ".dsc" ) . join ( "AdaptedResourcesLookupTable.json" ) . display ( ) . to_string ( )
601
+ }
0 commit comments