upstream_rs/services/builder/profiles/
rust.rs1use std::path::{Path, PathBuf};
2use std::process::Command;
3use std::{fs, str::FromStr};
4
5use anyhow::{Result, anyhow, bail};
6
7use crate::services::builder::{
8 BuildProfile,
9 profiles::{BuildProfileHandler, emit_line_callback, run_command_with_line_callback},
10};
11
12pub struct RustProfile;
13
14impl RustProfile {
15 fn binary_name(package_name: &str) -> String {
16 #[cfg(windows)]
17 {
18 format!("{package_name}.exe")
19 }
20 #[cfg(not(windows))]
21 {
22 package_name.to_string()
23 }
24 }
25
26 fn find_project_dir(workspace: &Path) -> Option<PathBuf> {
27 if workspace.join("Cargo.toml").is_file() {
28 Some(workspace.to_path_buf())
29 } else {
30 None
31 }
32 }
33
34 fn has_multiple_declared_bins(project_dir: &Path) -> bool {
35 let cargo_toml_path = project_dir.join("Cargo.toml");
36 let Ok(cargo_toml) = fs::read_to_string(&cargo_toml_path) else {
37 return false;
38 };
39
40 let Ok(parsed) = toml::Value::from_str(&cargo_toml) else {
41 return false;
42 };
43
44 parsed
45 .get("bin")
46 .and_then(toml::Value::as_array)
47 .is_some_and(|bins| bins.len() > 1)
48 }
49}
50
51impl BuildProfileHandler for RustProfile {
52 fn profile(&self) -> BuildProfile {
53 BuildProfile::Rust
54 }
55
56 fn detect(&self, workspace: &Path) -> bool {
57 Self::find_project_dir(workspace).is_some()
58 }
59
60 fn run_build(
61 &self,
62 workspace: &Path,
63 package_name: &str,
64 line_callback: &mut Option<&mut dyn FnMut(&str)>,
65 ) -> Result<PathBuf> {
66 let project_dir = Self::find_project_dir(workspace).ok_or_else(|| {
67 anyhow!(
68 "Could not find Cargo.toml in repository root '{}'.",
69 workspace.display()
70 )
71 })?;
72
73 let status = if Self::has_multiple_declared_bins(&project_dir) {
74 emit_line_callback(line_callback, "Running cargo build --release --bin ...");
75 run_command_with_line_callback(
76 Command::new("cargo")
77 .arg("build")
78 .arg("--release")
79 .arg("--bin")
80 .arg(package_name)
81 .current_dir(&project_dir),
82 "Failed to run 'cargo build --release --bin <name>'. Is Cargo installed?",
83 line_callback,
84 )?
85 } else {
86 emit_line_callback(line_callback, "Running cargo build --release ...");
87 run_command_with_line_callback(
88 Command::new("cargo")
89 .arg("build")
90 .arg("--release")
91 .current_dir(&project_dir),
92 "Failed to run 'cargo build --release'. Is Cargo installed?",
93 line_callback,
94 )?
95 };
96
97 if !status.success() {
98 bail!("Cargo build failed for '{}'", package_name);
99 }
100
101 let candidate = project_dir
102 .join("target")
103 .join("release")
104 .join(Self::binary_name(package_name));
105
106 if !candidate.exists() {
107 return Err(anyhow!(
108 "Rust build succeeded but artifact was not found at '{}'",
109 candidate.display()
110 ));
111 }
112
113 Ok(candidate)
114 }
115}