use serde::Deserialize; use serde::Serialize; use std::path::PathBuf; use std::process::Command; /// Read cargo metadata by running `cargo metadata` with the given manifest path. pub fn read_metadata(cargo_toml: PathBuf) -> Result { let output = Command::new("cargo") .arg("metadata") .arg("--format-version") .arg("1") .arg("--manifest-path") .arg(cargo_toml) .output()?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); return Err(std::io::Error::new( std::io::ErrorKind::Other, format!("cargo metadata failed: {}", stderr), )); } let lock_file: CargoLockFile = serde_json::from_slice(&output.stdout) .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?; Ok(lock_file) } /// A cargo metadata lock file that serde can serialize and deserialize. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct CargoLockFile { pub packages: Vec, #[serde(rename = "workspace_members")] pub workspace_members: Vec, #[serde(rename = "workspace_default_members")] pub workspace_default_members: Vec, pub resolve: Resolve, #[serde(rename = "target_directory")] pub target_directory: String, #[serde(rename = "build_directory")] pub build_directory: String, pub version: u64, #[serde(rename = "workspace_root")] pub workspace_root: String, pub metadata: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Package { pub name: String, pub version: String, pub id: String, pub license: Option, #[serde(rename = "license_file")] pub license_file: Option, pub description: Option, pub source: Option, pub dependencies: Vec, pub targets: Vec, pub features: std::collections::BTreeMap>, #[serde(rename = "manifest_path")] pub manifest_path: String, pub metadata: Option, pub publish: Option, pub authors: Vec, pub categories: Vec, pub keywords: Vec, pub readme: Option, pub repository: Option, pub homepage: Option, pub documentation: Option, pub edition: String, pub links: Option, #[serde(rename = "default_run")] pub default_run: Option, #[serde(rename = "rust_version")] pub rust_version: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Dependency { pub name: String, pub source: Option, pub req: String, pub kind: Option, pub rename: Option, pub optional: bool, #[serde(rename = "uses_default_features")] pub uses_default_features: bool, pub features: Vec, pub target: Option, pub registry: Option, #[serde(skip_serializing_if = "Option::is_none")] pub path: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Target { pub kind: Vec, #[serde(rename = "crate_types")] pub crate_types: Vec, pub name: String, #[serde(rename = "src_path")] pub src_path: String, pub edition: String, pub doc: bool, pub doctest: bool, pub test: bool, #[serde(skip_serializing_if = "Option::is_none")] #[serde(rename = "required-features")] pub required_features: Option>, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Resolve { pub nodes: Vec, pub root: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ResolveNode { pub id: String, pub dependencies: Vec, pub deps: Vec, pub features: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DepInfo { pub name: String, pub pkg: String, #[serde(rename = "dep_kinds")] pub dep_kinds: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] pub struct DepKind { pub kind: Option, pub target: Option, }