Skip to content

Commit 2a74f92

Browse files
committed
port mod metadata to cargo_metadata
1 parent d7582f1 commit 2a74f92

File tree

1 file changed

+93
-110
lines changed

1 file changed

+93
-110
lines changed

crates/cargo-gpu/src/metadata.rs

Lines changed: 93 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//! Get config from the shader crate's `Cargo.toml` `[*.metadata.rust-gpu.*]`
22
3+
use cargo_metadata::MetadataCommand;
34
use serde_json::Value;
45

56
/// `Metadata` refers to the `[metadata.*]` section of `Cargo.toml` that `cargo` formally
@@ -15,37 +16,17 @@ impl Metadata {
1516
/// First we generate the CLI arg defaults as JSON. Then on top of those we merge any config
1617
/// from the workspace `Cargo.toml`, then on top of those we merge any config from the shader
1718
/// crate's `Cargo.toml`.
18-
pub fn as_json(path: &std::path::PathBuf) -> anyhow::Result<serde_json::Value> {
19+
pub fn as_json(path: &std::path::PathBuf) -> anyhow::Result<Value> {
1920
let cargo_json = Self::get_cargo_toml_as_json(path)?;
2021
let config = Self::merge_configs(&cargo_json, path)?;
2122
Ok(config)
2223
}
2324

24-
/// Convert JSON keys from kebab case to snake case. Eg: `a-b` to `a_b`.
25-
///
26-
/// Detection of keys for serde deserialization must match the case in the Rust structs.
27-
/// However clap defaults to detecting CLI args in kebab case. So here we do the conversion.
28-
fn keys_to_snake_case(json: &mut serde_json::Value) {
29-
let serde_json::Value::Object(object) = json else {
30-
return;
31-
};
32-
33-
*object = core::mem::take(object)
34-
.into_iter()
35-
.map(|(key, mut value)| {
36-
if let serde_json::Value::Object(_) = value {
37-
Self::keys_to_snake_case(&mut value);
38-
}
39-
(key.replace('-', "_"), value)
40-
})
41-
.collect();
42-
}
43-
4425
/// Merge the various source of config: defaults, workspace and shader crate.
4526
fn merge_configs(
46-
cargo_json: &serde_json::Value,
27+
cargo_json: &cargo_metadata::Metadata,
4728
path: &std::path::Path,
48-
) -> anyhow::Result<serde_json::Value> {
29+
) -> anyhow::Result<Value> {
4930
let mut metadata = crate::config::Config::defaults_as_json()?;
5031
crate::config::Config::json_merge(
5132
&mut metadata,
@@ -85,74 +66,62 @@ impl Metadata {
8566
/// Convert a `Cargo.toml` to JSON
8667
//
8768
// TODO: reuse for getting the default `rust-gpu` source and toolchain.
88-
fn get_cargo_toml_as_json(path: &std::path::PathBuf) -> anyhow::Result<serde_json::Value> {
89-
let cargo_toml_path = path.join("Cargo.toml");
90-
if !cargo_toml_path.exists() {
91-
anyhow::bail!("{path:?} must be a shader crate directory");
92-
}
93-
94-
log::debug!("Querying Cargo metadata for {cargo_toml_path:?}");
95-
let output_cargo = std::process::Command::new("cargo")
96-
.args([
97-
"metadata",
98-
"--no-deps",
99-
"--manifest-path",
100-
cargo_toml_path.display().to_string().as_ref(),
101-
])
102-
.output()?;
103-
anyhow::ensure!(
104-
output_cargo.status.success(),
105-
"could not run `cargo metadata` on {cargo_toml_path:?}"
106-
);
107-
108-
Ok(serde_json::from_slice(&output_cargo.stdout)?)
69+
fn get_cargo_toml_as_json(
70+
path: &std::path::PathBuf,
71+
) -> anyhow::Result<cargo_metadata::Metadata> {
72+
Ok(MetadataCommand::new().current_dir(path).exec()?)
10973
}
11074

11175
/// Get any `rust-gpu` metadata set in the root workspace `Cargo.toml`
112-
fn get_workspace_metadata(json: &serde_json::Value) -> serde_json::Value {
113-
let empty_json_object = serde_json::json!({});
114-
let mut metadata = json
115-
.pointer("/metadata/rust-gpu")
116-
.unwrap_or(&empty_json_object)
117-
.clone();
118-
119-
Self::keys_to_snake_case(&mut metadata);
120-
metadata.clone()
76+
fn get_workspace_metadata(metadata: &cargo_metadata::Metadata) -> Value {
77+
Self::get_rust_gpu_from_metadata(&metadata.workspace_metadata)
12178
}
12279

12380
/// Get any `rust-gpu` metadata set in the crate's `Cargo.toml`
12481
fn get_crate_metadata(
125-
json: &serde_json::Value,
126-
path: &std::path::Path,
127-
) -> anyhow::Result<serde_json::Value> {
128-
let empty_json_object = serde_json::json!({});
129-
if let Some(serde_json::Value::Array(packages)) = json.pointer("/packages") {
130-
for package in packages {
131-
if let Some(serde_json::Value::String(manifest_path_dirty)) =
132-
package.pointer("/manifest_path")
133-
{
134-
let mut shader_crate_path = std::fs::canonicalize(path)?
135-
.join("Cargo.toml")
136-
.display()
137-
.to_string();
138-
139-
// Windows prefixs paths with `\\?\`
140-
shader_crate_path = shader_crate_path.replace(r"\\?\", "");
141-
let manifest_path = manifest_path_dirty.replace(r"\\?\", "");
142-
log::debug!("Matching shader crate path with manifest path: {shader_crate_path} == {manifest_path}?");
143-
if manifest_path == shader_crate_path {
144-
log::debug!("...matches! Getting metadata");
145-
let mut metadata = package
146-
.pointer("/metadata/rust-gpu")
147-
.unwrap_or(&empty_json_object)
148-
.clone();
149-
Self::keys_to_snake_case(&mut metadata);
150-
return Ok(metadata);
151-
}
152-
}
82+
json: &cargo_metadata::Metadata,
83+
shader_crate_path: &std::path::Path,
84+
) -> anyhow::Result<Value> {
85+
let shader_crate_path = std::fs::canonicalize(shader_crate_path)?.join("Cargo.toml");
86+
87+
for package in &json.packages {
88+
let manifest_path = std::fs::canonicalize(package.manifest_path.as_std_path())?;
89+
log::debug!(
90+
"Matching shader crate path with manifest path: '{}' == '{}'?",
91+
shader_crate_path.display(),
92+
manifest_path.display()
93+
);
94+
if manifest_path == shader_crate_path {
95+
log::debug!("...matches! Getting metadata");
96+
return Ok(Self::get_rust_gpu_from_metadata(&package.metadata));
15397
}
15498
}
155-
Ok(empty_json_object)
99+
Ok(serde_json::json!({}))
100+
}
101+
102+
fn get_rust_gpu_from_metadata(metadata: &Value) -> Value {
103+
Self::keys_to_snake_case(
104+
metadata
105+
.pointer("/rust-gpu")
106+
.cloned()
107+
.unwrap_or(Value::Null),
108+
)
109+
}
110+
111+
/// Convert JSON keys from kebab case to snake case. Eg: `a-b` to `a_b`.
112+
///
113+
/// Detection of keys for serde deserialization must match the case in the Rust structs.
114+
/// However clap defaults to detecting CLI args in kebab case. So here we do the conversion.
115+
fn keys_to_snake_case(json: Value) -> Value {
116+
match json {
117+
Value::Object(object) => Value::Object(
118+
object
119+
.into_iter()
120+
.map(|(key, value)| (key.replace('-', "_"), Self::keys_to_snake_case(value)))
121+
.collect(),
122+
),
123+
e => e,
124+
}
156125
}
157126
}
158127

@@ -163,59 +132,73 @@ impl Metadata {
163132
#[cfg(test)]
164133
mod test {
165134
use super::*;
135+
use std::path::Path;
166136

167137
#[test_log::test]
168138
fn generates_defaults() {
169-
let json = serde_json::json!({});
170-
let configs = Metadata::merge_configs(&json, std::path::Path::new("./")).unwrap();
171-
assert_eq!(configs["build"]["release"], serde_json::Value::Bool(true));
139+
let mut metadata = MetadataCommand::new()
140+
.current_dir(env!("CARGO_MANIFEST_DIR"))
141+
.exec()
142+
.unwrap();
143+
metadata.packages.first_mut().unwrap().metadata = serde_json::json!({});
144+
let configs = Metadata::merge_configs(&metadata, Path::new("./")).unwrap();
145+
assert_eq!(configs["build"]["release"], Value::Bool(true));
172146
assert_eq!(
173147
configs["install"]["auto_install_rust_toolchain"],
174-
serde_json::Value::Bool(false)
148+
Value::Bool(false)
175149
);
176150
}
177151

178152
#[test_log::test]
179153
fn can_override_config_from_workspace_toml() {
180-
let json = serde_json::json!(
181-
{ "metadata": { "rust-gpu": {
154+
let mut metadata = MetadataCommand::new()
155+
.current_dir(env!("CARGO_MANIFEST_DIR"))
156+
.exec()
157+
.unwrap();
158+
metadata.workspace_metadata = serde_json::json!({
159+
"rust-gpu": {
182160
"build": {
183161
"release": false
184162
},
185163
"install": {
186164
"auto-install-rust-toolchain": true
187165
}
188-
}}}
189-
);
190-
let configs = Metadata::merge_configs(&json, std::path::Path::new("./")).unwrap();
191-
assert_eq!(configs["build"]["release"], serde_json::Value::Bool(false));
166+
}
167+
});
168+
let configs = Metadata::merge_configs(&metadata, Path::new("./")).unwrap();
169+
assert_eq!(configs["build"]["release"], Value::Bool(false));
192170
assert_eq!(
193171
configs["install"]["auto_install_rust_toolchain"],
194-
serde_json::Value::Bool(true)
172+
Value::Bool(true)
195173
);
196174
}
197175

198176
#[test_log::test]
199177
fn can_override_config_from_crate_toml() {
200-
let marker = std::path::Path::new("./Cargo.toml");
201-
let json = serde_json::json!(
202-
{ "packages": [{
203-
"metadata": { "rust-gpu": {
204-
"build": {
205-
"release": false
206-
},
207-
"install": {
208-
"auto-install-rust-toolchain": true
209-
}
210-
}},
211-
"manifest_path": std::fs::canonicalize(marker).unwrap()
212-
}]}
213-
);
214-
let configs = Metadata::merge_configs(&json, marker.parent().unwrap()).unwrap();
215-
assert_eq!(configs["build"]["release"], serde_json::Value::Bool(false));
178+
let mut metadata = MetadataCommand::new()
179+
.current_dir(env!("CARGO_MANIFEST_DIR"))
180+
.exec()
181+
.unwrap();
182+
let cargo_gpu = metadata
183+
.packages
184+
.iter_mut()
185+
.find(|p| p.name.contains("cargo-gpu"))
186+
.unwrap();
187+
cargo_gpu.metadata = serde_json::json!({
188+
"rust-gpu": {
189+
"build": {
190+
"release": false
191+
},
192+
"install": {
193+
"auto-install-rust-toolchain": true
194+
}
195+
}
196+
});
197+
let configs = Metadata::merge_configs(&metadata, Path::new(".")).unwrap();
198+
assert_eq!(configs["build"]["release"], Value::Bool(false));
216199
assert_eq!(
217200
configs["install"]["auto_install_rust_toolchain"],
218-
serde_json::Value::Bool(true)
201+
Value::Bool(true)
219202
);
220203
}
221204
}

0 commit comments

Comments
 (0)