1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
use std::{error, path, process}; pub fn fetch() -> Result<Metadata, Box<dyn error::Error>> { let json = fetch_cargo_metadata_json()?; let json_value = json::parse(&json)?; Ok(Metadata::from(json_value)) } fn fetch_cargo_metadata_json() -> Result<String, Box<dyn error::Error>> { let mut command = process::Command::new(crate::cargo_cmd()); command.arg("metadata").arg("--format-version").arg("1"); let output = command.stderr(process::Stdio::inherit()).output()?; if !output.status.success() { return Err("`cargo metadata` returned a non-zero status".into()); } Ok(String::from_utf8(output.stdout)?) } #[derive(Clone)] pub struct Dependency { pub name: String, pub rename: Option<String>, pub optional: bool, } impl From<json::JsonValue> for Dependency { fn from(json_value: json::JsonValue) -> Self { let name = json_value["name"].as_str().unwrap().to_owned(); let rename = json_value["rename"].as_str().map(|s| s.to_string()); let optional = json_value["optional"].as_bool().unwrap(); Dependency { name, rename, optional } } } #[derive(Clone)] pub struct Package { pub id: String, pub name: String, pub manifest_path: path::PathBuf, pub dependencies: Vec<Dependency>, pub features: Vec<String>, pub skip_feature_sets: Vec<Vec<String>>, pub skip_optional_dependencies: bool, pub extra_features: Vec<String>, } impl From<json::JsonValue> for Package { fn from(json_value: json::JsonValue) -> Self { let id = json_value["id"].as_str().unwrap().to_owned(); let name = json_value["name"].as_str().unwrap().to_owned(); let manifest_path = path::PathBuf::from(json_value["manifest_path"].as_str().unwrap().to_owned()); let dependencies = json_value["dependencies"] .members() .map(|member| Dependency::from(member.to_owned())) .collect(); let features = json_value["features"] .entries() .map(|(k, _v)| k.to_owned()) .collect(); let skip_feature_sets: Vec<Vec<String>> = json_value["metadata"] ["cargo-all-features"] ["skip_feature_sets"] .members() .map(|member| { member .members() .map(|feature| feature.as_str().unwrap().to_owned()) .collect() }) .collect(); let skip_optional_dependencies: bool = json_value["metadata"] ["cargo-all-features"] ["skip_optional_dependencies"] .as_bool() .unwrap_or(false); let extra_features: Vec<String> = json_value["metadata"] ["cargo-all-features"] ["extra_features"] .members() .map(|member| member.as_str().unwrap().to_owned()) .collect(); Package { id, name, manifest_path, dependencies, features, skip_feature_sets, skip_optional_dependencies, extra_features, } } } #[derive(Clone)] pub struct Metadata { pub workspace_root: path::PathBuf, pub workspace_members: Vec<String>, pub packages: Vec<Package>, } impl From<json::JsonValue> for Metadata { fn from(json_value: json::JsonValue) -> Self { let workspace_root = path::PathBuf::from(json_value["workspace_root"].as_str().unwrap().to_owned()); let workspace_members = json_value["workspace_members"] .members() .map(|member| member.as_str().unwrap().to_owned()) .collect(); let packages = json_value["packages"] .members() .map(|member| Package::from(member.to_owned())) .collect(); Metadata { workspace_root, workspace_members, packages, } } }