use std::path::Path;
use std::time::Duration;
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use strum::Display;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display)]
#[serde(rename_all = "snake_case")]
#[strum(serialize_all = "snake_case")]
pub enum ExecutorKind {
Local,
Sandbox,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommandOutput {
pub stdout: String,
pub stderr: String,
pub exit_code: i32,
}
impl CommandOutput {
pub fn success(&self) -> bool {
self.exit_code == 0
}
}
#[derive(Debug, thiserror::Error)]
pub enum ExecutorError {
#[error("unsupported executor kind: {0}")]
UnsupportedKind(ExecutorKind),
#[error("executor error: {0}")]
Other(String),
}
pub type Result<T, E = ExecutorError> = std::result::Result<T, E>;
#[async_trait]
pub trait ToolExecutor: Send + Sync {
async fn kind(&self) -> ExecutorKind;
async fn read_file(&self, path: &Path) -> Result<String>;
async fn write_file(&self, path: &Path, content: &str) -> Result<()>;
async fn run_command(
&self,
cwd: &Path,
argv: &[String],
timeout: Duration,
) -> Result<CommandOutput>;
async fn list_dir(&self, path: &Path) -> Result<Vec<String>>;
async fn grep(&self, pattern: &str, path: &Path) -> Result<Vec<String>>;
async fn find(&self, glob: &str, path: &Path) -> Result<Vec<String>>;
async fn git(&self, args: &[String]) -> Result<CommandOutput>;
}
#[cfg(test)]
tests_bridge_macro::tests_bridge!("executor");