11//! Get config from the shader crate's `Cargo.toml` `[*.metadata.rust-gpu.*]`
22
3+ use std:: collections:: HashMap ;
4+
5+ use anyhow:: Context as _;
36use cargo_metadata:: MetadataCommand ;
47use serde_json:: Value ;
58
6- /// `Metadata` refers to the `[metadata.*]` section of `Cargo.toml` that `cargo` formally
9+ /// A cache of metadata from various `Cargo.toml` files.
10+ ///
11+ /// "Metadata" refers to the `[metadata.*]` section of `Cargo.toml` that `cargo` formally
712/// ignores so that packages can implement their own behaviour with it.
8- #[ derive( Debug ) ]
9- pub struct Metadata ;
13+ #[ derive( Debug , Default ) ]
14+ pub struct MetadataCache {
15+ /// Cached result of `MetadataCommand::new().exec()`.
16+ inner : HashMap < std:: path:: PathBuf , cargo_metadata:: Metadata > ,
17+ }
18+
19+ impl MetadataCache {
20+ /// Return the cached cargo metadata for the Cargo.toml at the given path,
21+ /// or find it, populate the cache with it and return it.
22+ fn get_metadata (
23+ & mut self ,
24+ maybe_path_to_manifest_dir : Option < & std:: path:: Path > ,
25+ ) -> anyhow:: Result < & cargo_metadata:: Metadata > {
26+ let path = if let Some ( path) = maybe_path_to_manifest_dir {
27+ path. to_path_buf ( )
28+ } else {
29+ std:: env:: current_dir ( ) . context ( "cannot determine the current working directory" ) ?
30+ } ;
31+
32+ if !self . inner . contains_key ( & path) {
33+ let metadata = MetadataCommand :: new ( ) . current_dir ( & path) . exec ( ) ?;
34+ self . inner . insert ( path. clone ( ) , metadata) ;
35+ }
36+
37+ self . inner . get ( & path) . context ( "unreachable" )
38+ }
39+
40+ /// Resolve a package name to a crate directory.
41+ ///
42+ /// ## Errors
43+ /// * if fetching cargo metadata fails.
44+ /// * if no packages are listed in the cargo metadata.
45+ /// * if the manifest path has no parent.
46+ pub fn resolve_package_to_shader_crate (
47+ & mut self ,
48+ package : & str ,
49+ ) -> anyhow:: Result < std:: path:: PathBuf > {
50+ log:: debug!( "resolving package '{package}' to shader crate" ) ;
51+ let metadata = self . get_metadata ( None ) ?;
52+
53+ let meta_package = metadata
54+ . packages
55+ . iter ( )
56+ . find ( |pkg| pkg. name . as_str ( ) == package)
57+ . context ( "Package not found in metadata" ) ?;
58+ let shader_crate_path: std:: path:: PathBuf = meta_package
59+ . manifest_path
60+ . parent ( )
61+ . context ( "manifest is missing a parent directory" ) ?
62+ . to_path_buf ( )
63+ . into ( ) ;
64+ log:: debug!(
65+ " determined shader crate path to be '{}'" ,
66+ shader_crate_path. display( )
67+ ) ;
68+ Ok ( shader_crate_path)
69+ }
1070
11- impl Metadata {
1271 /// Convert `rust-gpu`-specific sections in `Cargo.toml` to `clap`-compatible arguments.
1372 /// The section in question is: `[package.metadata.rust-gpu.*]`. See the `shader-crate-template`
1473 /// for an example.
1574 ///
1675 /// First we generate the CLI arg defaults as JSON. Then on top of those we merge any config
1776 /// from the workspace `Cargo.toml`, then on top of those we merge any config from the shader
1877 /// crate's `Cargo.toml`.
19- pub fn as_json ( path : & std:: path:: PathBuf ) -> anyhow:: Result < Value > {
20- let cargo_json = Self :: get_cargo_toml_as_json ( path) ?;
78+ ///
79+ /// ## Errors
80+ /// Errors if cargo metadata cannot be found or if it cannot be operated on.
81+ pub fn as_json ( & mut self , path : & std:: path:: Path ) -> anyhow:: Result < Value > {
82+ log:: debug!( "reading package metadata from {}" , path. display( ) ) ;
83+ let cargo_json = self . get_cargo_toml_as_json ( path) ?;
2184 let config = Self :: merge_configs ( & cargo_json, path) ?;
2285 Ok ( config)
2386 }
@@ -27,6 +90,7 @@ impl Metadata {
2790 cargo_json : & cargo_metadata:: Metadata ,
2891 path : & std:: path:: Path ,
2992 ) -> anyhow:: Result < Value > {
93+ log:: debug!( "merging cargo metadata from {}" , path. display( ) ) ;
3094 let mut metadata = crate :: config:: Config :: defaults_as_json ( ) ?;
3195 crate :: config:: Config :: json_merge (
3296 & mut metadata,
@@ -65,9 +129,10 @@ impl Metadata {
65129
66130 /// Convert a `Cargo.toml` to JSON
67131 fn get_cargo_toml_as_json (
68- path : & std:: path:: PathBuf ,
132+ & mut self ,
133+ path : & std:: path:: Path ,
69134 ) -> anyhow:: Result < cargo_metadata:: Metadata > {
70- Ok ( MetadataCommand :: new ( ) . current_dir ( path) . exec ( ) ? )
135+ self . get_metadata ( Some ( path) ) . cloned ( )
71136 }
72137
73138 /// Get any `rust-gpu` metadata set in the crate's `Cargo.toml`
@@ -136,7 +201,7 @@ mod test {
136201 . exec ( )
137202 . unwrap ( ) ;
138203 metadata. packages . first_mut ( ) . unwrap ( ) . metadata = serde_json:: json!( { } ) ;
139- let configs = Metadata :: merge_configs ( & metadata, Path :: new ( "./" ) ) . unwrap ( ) ;
204+ let configs = MetadataCache :: merge_configs ( & metadata, Path :: new ( "./" ) ) . unwrap ( ) ;
140205 assert_eq ! ( configs[ "build" ] [ "release" ] , Value :: Bool ( true ) ) ;
141206 assert_eq ! (
142207 configs[ "install" ] [ "auto_install_rust_toolchain" ] ,
@@ -160,7 +225,7 @@ mod test {
160225 }
161226 }
162227 } ) ;
163- let configs = Metadata :: merge_configs ( & metadata, Path :: new ( "./" ) ) . unwrap ( ) ;
228+ let configs = MetadataCache :: merge_configs ( & metadata, Path :: new ( "./" ) ) . unwrap ( ) ;
164229 assert_eq ! ( configs[ "build" ] [ "release" ] , Value :: Bool ( false ) ) ;
165230 assert_eq ! (
166231 configs[ "install" ] [ "auto_install_rust_toolchain" ] ,
@@ -189,7 +254,7 @@ mod test {
189254 }
190255 }
191256 } ) ;
192- let configs = Metadata :: merge_configs ( & metadata, Path :: new ( "." ) ) . unwrap ( ) ;
257+ let configs = MetadataCache :: merge_configs ( & metadata, Path :: new ( "." ) ) . unwrap ( ) ;
193258 assert_eq ! ( configs[ "build" ] [ "release" ] , Value :: Bool ( false ) ) ;
194259 assert_eq ! (
195260 configs[ "install" ] [ "auto_install_rust_toolchain" ] ,
0 commit comments