cargo_codesign/
discovery.rs1use std::path::PathBuf;
2
3#[derive(Debug, thiserror::Error)]
4pub enum DiscoveryError {
5 #[error("failed to parse cargo metadata: {0}")]
6 ParseError(#[from] serde_json::Error),
7 #[error("failed to run cargo metadata: {0}")]
8 CargoMetadataFailed(String),
9 #[error("subprocess error: {0}")]
10 Subprocess(#[from] crate::subprocess::SubprocessError),
11}
12
13#[derive(Debug)]
14pub struct BinaryTarget {
15 pub name: String,
16 pub package_name: String,
17 pub package_version: String,
18 target_directory: PathBuf,
19}
20
21impl BinaryTarget {
22 pub fn release_path(&self) -> PathBuf {
24 self.target_directory.join("release").join(&self.name)
25 }
26
27 pub fn signed_release_path(&self) -> PathBuf {
29 self.target_directory
30 .join("signed")
31 .join("release")
32 .join(&self.name)
33 }
34}
35
36pub fn parse_metadata(json: &str) -> Result<Vec<BinaryTarget>, DiscoveryError> {
39 let meta: serde_json::Value = serde_json::from_str(json)?;
40
41 let target_directory = meta["target_directory"]
42 .as_str()
43 .unwrap_or("target")
44 .to_string();
45
46 let packages = meta["packages"].as_array();
47 let Some(packages) = packages else {
48 return Ok(Vec::new());
49 };
50
51 let mut binaries = Vec::new();
52
53 for pkg in packages {
54 let pkg_name = pkg["name"].as_str().unwrap_or_default().to_string();
55 let pkg_version = pkg["version"].as_str().unwrap_or_default().to_string();
56
57 let Some(targets) = pkg["targets"].as_array() else {
58 continue;
59 };
60
61 for target in targets {
62 let kinds = target["kind"].as_array();
63 let is_bin = kinds
64 .is_some_and(|kinds| kinds.iter().any(|k| k.as_str().is_some_and(|s| s == "bin")));
65
66 if is_bin {
67 let name = target["name"].as_str().unwrap_or_default().to_string();
68 binaries.push(BinaryTarget {
69 name,
70 package_name: pkg_name.clone(),
71 package_version: pkg_version.clone(),
72 target_directory: PathBuf::from(&target_directory),
73 });
74 }
75 }
76 }
77
78 Ok(binaries)
79}
80
81pub fn discover_binaries() -> Result<Vec<BinaryTarget>, DiscoveryError> {
83 let output = crate::subprocess::run(
84 "cargo",
85 &["metadata", "--format-version", "1", "--no-deps"],
86 false,
87 )?;
88 if !output.success {
89 return Err(DiscoveryError::CargoMetadataFailed(output.stderr));
90 }
91 parse_metadata(&output.stdout)
92}