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 cargo_toml = match fs::read_to_string(&cargo_toml_path) {
37 Ok(contents) => contents,
38 Err(_) => return false,
39 };
40
41 let parsed = match toml::Value::from_str(&cargo_toml) {
42 Ok(value) => value,
43 Err(_) => return false,
44 };
45
46 parsed
47 .get("bin")
48 .and_then(toml::Value::as_array)
49 .is_some_and(|bins| bins.len() > 1)
50 }
51}
52
53impl BuildProfileHandler for RustProfile {
54 fn profile(&self) -> BuildProfile {
55 BuildProfile::Rust
56 }
57
58 fn detect(&self, workspace: &Path) -> bool {
59 Self::find_project_dir(workspace).is_some()
60 }
61
62 fn run_build(
63 &self,
64 workspace: &Path,
65 package_name: &str,
66 line_callback: &mut Option<&mut dyn FnMut(&str)>,
67 ) -> Result<PathBuf> {
68 let project_dir = Self::find_project_dir(workspace).ok_or_else(|| {
69 anyhow!(
70 "Could not find Cargo.toml in repository root '{}'.",
71 workspace.display()
72 )
73 })?;
74
75 let status = if Self::has_multiple_declared_bins(&project_dir) {
76 emit_line_callback(line_callback, "Running cargo build --release --bin ...");
77 run_command_with_line_callback(
78 Command::new("cargo")
79 .arg("build")
80 .arg("--release")
81 .arg("--bin")
82 .arg(package_name)
83 .current_dir(&project_dir),
84 "Failed to run 'cargo build --release --bin <name>'. Is Cargo installed?",
85 line_callback,
86 )?
87 } else {
88 emit_line_callback(line_callback, "Running cargo build --release ...");
89 run_command_with_line_callback(
90 Command::new("cargo")
91 .arg("build")
92 .arg("--release")
93 .current_dir(&project_dir),
94 "Failed to run 'cargo build --release'. Is Cargo installed?",
95 line_callback,
96 )?
97 };
98
99 if !status.success() {
100 bail!("Cargo build failed for '{}'", package_name);
101 }
102
103 let candidate = project_dir
104 .join("target")
105 .join("release")
106 .join(Self::binary_name(package_name));
107
108 if !candidate.exists() {
109 return Err(anyhow!(
110 "Rust build succeeded but artifact was not found at '{}'",
111 candidate.display()
112 ));
113 }
114
115 Ok(candidate)
116 }
117}