soma_codemode/git/
command.rs1use std::path::PathBuf;
2
3use tokio::process::Command;
4
5use crate::ToolError;
6
7use super::output::cap_output;
8use super::safety::safe_git_env;
9
10#[derive(Debug, Clone)]
11pub struct GitCommand {
12 cwd: PathBuf,
13 args: Vec<String>,
14}
15
16impl GitCommand {
17 pub fn new(cwd: impl Into<PathBuf>, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
18 Self {
19 cwd: cwd.into(),
20 args: args.into_iter().map(Into::into).collect(),
21 }
22 }
23
24 pub async fn run(&self) -> Result<String, ToolError> {
25 let mut command = Command::new("git");
26 command.current_dir(&self.cwd).args(&self.args).env_clear();
27 for (key, value) in safe_git_env() {
28 command.env(key, value);
29 }
30 let output = command
31 .output()
32 .await
33 .map_err(|err| ToolError::internal_message(format!("git failed: {err}")))?;
34 if !output.status.success() {
35 let stderr = cap_output(&output.stderr, 8 * 1024);
36 return Err(ToolError::Sdk {
37 sdk_kind: "upstream_error".to_string(),
38 message: if stderr.trim().is_empty() {
39 format!("git exited with status {}", output.status)
40 } else {
41 format!("git exited with status {}: {stderr}", output.status)
42 },
43 });
44 }
45 Ok(cap_output(&output.stdout, 64 * 1024))
46 }
47
48 pub fn args(&self) -> &[String] {
49 &self.args
50 }
51}