@@ -8,11 +8,13 @@ use crate::dscresources::resource_manifest::{import_manifest, validate_semver, K
88use crate :: dscresources:: command_resource:: invoke_command;
99use crate :: dscerror:: DscError ;
1010use indicatif:: ProgressStyle ;
11+ use linked_hash_map:: LinkedHashMap ;
1112use regex:: RegexBuilder ;
1213use semver:: Version ;
13- use std:: collections:: { BTreeMap , HashSet } ;
14+ use std:: collections:: { BTreeMap , HashSet , HashMap } ;
1415use std:: env;
1516use std:: ffi:: OsStr ;
17+ use std:: fs;
1618use std:: fs:: File ;
1719use std:: io:: BufReader ;
1820use std:: path:: { Path , PathBuf } ;
@@ -279,6 +281,7 @@ impl ResourceDiscovery for CommandDiscovery {
279281 }
280282
281283 self . adapted_resources = adapted_resources;
284+
282285 Ok ( ( ) )
283286 }
284287
@@ -294,6 +297,11 @@ impl ResourceDiscovery for CommandDiscovery {
294297 } else {
295298 self . discover_resources ( "*" ) ?;
296299 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
297305 resources. append ( & mut self . adapted_resources ) ;
298306 }
299307
@@ -334,7 +342,8 @@ impl ResourceDiscovery for CommandDiscovery {
334342 debug ! ( "Found {} matching non-adapter-based resources" , found_resources. len( ) ) ;
335343
336344 // 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 {
338347 // TODO: handle version requirements
339348 let Some ( adapter) = adapters. first ( ) else {
340349 // skip if no adapters
@@ -353,6 +362,8 @@ impl ResourceDiscovery for CommandDiscovery {
353362 }
354363
355364 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 ) ;
356367
357368 // now go through the adapter resources and add them to the list of resources
358369 for ( adapted_name, adapted_resource) in & self . adapted_resources {
@@ -496,3 +507,95 @@ fn load_manifest(path: &Path) -> Result<DscResource, DscError> {
496507
497508 Ok ( resource)
498509}
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