volition_core/tools/
cargo.rs1use super::CommandOutput;
4use crate::utils::truncate_string; use anyhow::{Context, Result};
6use std::path::Path;
7use std::process::{Command, Stdio};
8use tracing::{debug, info};
9
10pub async fn execute_cargo_command(
11 command_name: &str,
12 command_args: &[String],
13 working_dir: &Path,
14) -> Result<CommandOutput> {
15 let full_command_log = format!("cargo {} {}", command_name, command_args.join(" "));
16 let command_display = truncate_string(&full_command_log, 60);
18 info!(
19 "Executing cargo command: {} in {:?}",
20 command_display, working_dir
22 );
23
24 let output = Command::new("cargo")
25 .current_dir(working_dir)
26 .arg(command_name)
27 .args(command_args)
28 .stdout(Stdio::piped())
29 .stderr(Stdio::piped())
30 .output()
31 .with_context(|| format!("Failed to execute cargo command: {}", full_command_log))?;
32
33 let stdout = String::from_utf8_lossy(&output.stdout).to_string();
34 let stderr = String::from_utf8_lossy(&output.stderr).to_string();
35 let status = output.status.code().unwrap_or(-1);
36
37 debug!("cargo {} exit status: {}", full_command_log, status);
38
39 Ok(CommandOutput {
40 status,
41 stdout,
42 stderr,
43 })
44}
45
46#[cfg(test)]
47mod tests {
48 use super::*;
49 use std::path::{Path, PathBuf};
50 use tempfile::tempdir;
51 use tokio;
52
53 fn test_working_dir() -> PathBuf {
54 tempdir()
55 .map(|d| {
56 let path = d.into_path();
57 path
58 })
59 .unwrap_or_else(|_| PathBuf::from("."))
60 }
61
62 #[tokio::test]
63 async fn test_execute_cargo_check() {
64 let working_dir = test_working_dir();
65 if working_dir != Path::new(".") {
66 std::fs::write(
67 working_dir.join("Cargo.toml"),
68 "[package]\nname = \"test_crate\"\nversion = \"0.1.0\"\nedition = \"2021\"\n",
69 )
70 .unwrap();
71 std::fs::create_dir(working_dir.join("src")).unwrap();
72 std::fs::write(working_dir.join("src/lib.rs"), "pub fn hello() {}").unwrap();
73 }
74
75 let result = execute_cargo_command("check", &[], &working_dir).await;
76 assert!(result.is_ok(), "cargo check failed: {:?}", result.err());
77 let output = result.unwrap();
78 println!("Output: {:?}", output);
79 assert_eq!(output.status, 0);
80 }
81
82 #[tokio::test]
83 async fn test_execute_cargo_build_fail_no_src() {
84 let working_dir = test_working_dir();
85 if working_dir != Path::new(".") {
86 std::fs::write(
87 working_dir.join("Cargo.toml"),
88 "[package]\nname = \"test_crate_fail\"\nversion = \"0.1.0\"\nedition = \"2021\"\n",
89 )
90 .unwrap();
91 }
92
93 if working_dir != Path::new(".") {
94 let result = execute_cargo_command("build", &[], &working_dir).await;
95 assert!(result.is_ok());
96 let output = result.unwrap();
97 println!("Output: {:?}", output);
98 assert_ne!(output.status, 0);
99 assert!(
100 output
101 .stderr
102 .contains("no targets specified in the manifest")
103 );
104 }
105 }
106}