use super::{
CommandDefinition, CommandExecutionError, CommandExecutionResult, HookContext, HookManager,
};
use std::collections::HashMap;
use std::sync::Arc;
pub struct CommandExecutor {
#[allow(dead_code)]
api_client: Option<crate::client::ApiClient>,
hook_manager: Arc<HookManager>,
}
impl CommandExecutor {
pub fn new() -> Self {
Self {
api_client: None,
hook_manager: Arc::new(HookManager::new()),
}
}
pub fn with_api_client(api_client: crate::client::ApiClient) -> Self {
Self {
api_client: Some(api_client),
hook_manager: Arc::new(HookManager::new()),
}
}
pub fn with_hooks(mut self, hooks: Vec<Box<dyn super::CommandHook + Send + Sync>>) -> Self {
if let Some(hook_manager) = Arc::get_mut(&mut self.hook_manager) {
for hook in hooks {
hook_manager.add_pre_hook(hook);
}
} else {
log::warn!("Cannot add hooks: hook manager has multiple references");
}
self
}
pub async fn execute(
&self,
definition: &CommandDefinition,
parameters: &HashMap<String, String>,
) -> Result<CommandExecutionResult, CommandExecutionError> {
self.execute_with_context(definition, parameters, "default", "default", "default", ".")
.await
}
pub async fn execute_with_context(
&self,
definition: &CommandDefinition,
parameters: &HashMap<String, String>,
command: &str,
user: &str,
role: &str,
working_directory: &str,
) -> Result<CommandExecutionResult, CommandExecutionError> {
let hook_context = HookContext {
command: command.to_string(),
parameters: parameters.clone(),
user: user.to_string(),
role: role.to_string(),
execution_mode: definition.execution_mode.clone(),
working_directory: std::path::PathBuf::from(working_directory),
};
self.hook_manager.execute_pre_hooks(&hook_context).await?;
let result = match self.execute_command_internal(definition, parameters).await {
Ok(result) => {
if let Err(hook_error) = self
.hook_manager
.execute_post_hooks(&hook_context, &result)
.await
{
eprintln!("Warning: Post-command hooks failed: {}", hook_error);
}
result
}
Err(e) => {
let failed_result = CommandExecutionResult {
command: command.to_string(),
execution_mode: definition.execution_mode.clone(),
exit_code: 1,
stdout: String::new(),
stderr: e.to_string(),
duration_ms: 0,
resource_usage: None,
};
if let Err(hook_error) = self
.hook_manager
.execute_post_hooks(&hook_context, &failed_result)
.await
{
eprintln!(
"Warning: Post-command hooks failed on error: {}",
hook_error
);
}
return Err(e);
}
};
Ok(result)
}
async fn execute_command_internal(
&self,
definition: &CommandDefinition,
parameters: &HashMap<String, String>,
) -> Result<CommandExecutionResult, CommandExecutionError> {
let executor = super::modes::create_executor(definition.execution_mode.clone());
executor.execute_command(definition, parameters).await
}
}
impl Default for CommandExecutor {
fn default() -> Self {
Self::new()
}
}