use std::ffi::OsString;
use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::{Context, Result, bail};
use serde::Deserialize;
#[derive(Deserialize)]
pub(super) struct Metadata {
pub(super) packages: Vec<Package>,
pub(super) workspace_members: Vec<String>,
pub(super) workspace_root: PathBuf,
pub(super) target_directory: PathBuf,
pub(super) resolve: Option<Resolve>,
}
#[derive(Deserialize)]
pub(super) struct Package {
pub(super) id: String,
pub(super) name: String,
pub(super) version: String,
pub(super) manifest_path: PathBuf,
pub(super) source: Option<String>,
pub(super) targets: Vec<Target>,
}
#[derive(Deserialize)]
pub(super) struct Target {
pub(super) kind: Vec<String>,
pub(super) crate_types: Vec<String>,
}
#[derive(Deserialize)]
pub(super) struct Resolve {
pub(super) nodes: Vec<Node>,
}
#[derive(Deserialize)]
pub(super) struct Node {
pub(super) id: String,
pub(super) deps: Vec<Dependency>,
}
#[derive(Deserialize)]
pub(super) struct Dependency {
pub(super) pkg: String,
pub(super) dep_kinds: Vec<DependencyKind>,
}
#[derive(Deserialize)]
pub(super) struct DependencyKind {
pub(super) kind: Option<String>,
}
pub(super) fn metadata(manifest: &Path, no_dependencies: bool) -> Result<Metadata> {
let mut command = Command::new(executable());
command
.args(["metadata", "--format-version", "1", "--manifest-path"])
.arg(manifest);
if no_dependencies {
command.arg("--no-deps");
}
let output = command.output().context("failed to run cargo metadata")?;
if !output.status.success() {
bail!(
"cargo metadata failed\n{}",
String::from_utf8_lossy(&output.stderr)
);
}
serde_json::from_slice(&output.stdout).context("Cargo returned invalid metadata")
}
pub(super) fn executable() -> OsString {
std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into())
}