Skip to main content

teaql_tool_extra/
cmd.rs

1use teaql_tool_core::{Result, TeaQLToolError};
2use std::process::Command;
3use wait_timeout::ChildExt;
4use std::time::Duration;
5
6#[derive(Debug, Clone)]
7pub struct CmdTool;
8
9impl CmdTool {
10    pub fn new() -> Self { Self }
11
12    pub fn run_with_timeout(&self, cmd_line: &str, timeout_secs: u64) -> Result<(String, String, i32)> {
13        let parts: Vec<&str> = cmd_line.split_whitespace().collect();
14        if parts.is_empty() {
15            return Err(TeaQLToolError::ExecutionError("Empty command".into()));
16        }
17        
18        let mut cmd = Command::new(parts[0]);
19        cmd.args(&parts[1..]);
20        cmd.stdout(std::process::Stdio::piped());
21        cmd.stderr(std::process::Stdio::piped());
22        
23        let mut child = cmd.spawn().map_err(|e| TeaQLToolError::ExecutionError(e.to_string()))?;
24        let timeout = Duration::from_secs(timeout_secs);
25        
26        match child.wait_timeout(timeout).map_err(|e| TeaQLToolError::ExecutionError(e.to_string()))? {
27            Some(status) => {
28                let out = child.wait_with_output().map_err(|e| TeaQLToolError::ExecutionError(e.to_string()))?;
29                let stdout = String::from_utf8_lossy(&out.stdout).to_string();
30                let stderr = String::from_utf8_lossy(&out.stderr).to_string();
31                Ok((stdout, stderr, status.code().unwrap_or(-1)))
32            }
33            None => {
34                let _ = child.kill();
35                let _ = child.wait();
36                Err(TeaQLToolError::ExecutionError("Command timed out".into()))
37            }
38        }
39    }
40}
41
42impl Default for CmdTool {
43    fn default() -> Self { Self::new() }
44}