use crate::{TerminalApp, get_error, get_info, get_warn};
use async_trait::async_trait;
use futures::future::BoxFuture;
use tokio::task::JoinHandle;
#[derive(Debug)]
pub struct CommandResult {
pub command: String,
pub output: String,
}
pub trait CommandHandler: Send + Sync + 'static {
fn execute(&mut self, app: &mut TerminalApp, args: &[&str]) -> String;
}
#[async_trait]
pub trait AsyncCommandHandler: Send + Sync + 'static {
async fn execute_async(&mut self, app: &mut TerminalApp, args: &[&str]) -> String;
fn box_clone(&self) -> Box<dyn AsyncCommandHandler>;
}
pub enum CommandHandlerType {
PubSync(Box<dyn CommandHandler>),
PubAsync(Box<dyn AsyncCommandHandler>),
}
#[derive(Debug)]
pub struct RunningCommand {
pub command: String,
pub handle: JoinHandle<String>,
}
impl CommandHandlerType {
pub fn as_sync(&self) -> bool {
matches!(self, CommandHandlerType::PubSync(_))
}
}
pub type UnknownCommandHandler = Box<dyn Fn(&str) -> String + Send + Sync + 'static>;
pub type AsyncUnknownCommandHandler =
Box<dyn Fn(&str) -> BoxFuture<'static, String> + Send + Sync + 'static>;
impl<F> CommandHandler for F
where
F: FnMut(&mut TerminalApp, &[&str]) -> String + Send + Sync + 'static,
{
fn execute(&mut self, app: &mut TerminalApp, args: &[&str]) -> String {
self(app, args)
}
}
pub async fn execute_command(app: &mut TerminalApp, command: &str) -> String {
let parts: Vec<&str> = command.split_whitespace().collect();
if parts.is_empty() {
return String::new();
}
let cmd_name = parts[0];
let args = &parts[1..];
if let Some(handler) = app.commands.get(cmd_name) {
match handler {
CommandHandlerType::PubSync(_) => {
if let Some(CommandHandlerType::PubSync(mut sync_handler)) =
app.commands.remove(cmd_name)
{
let result = sync_handler.execute(app, args);
app.commands.insert(
cmd_name.to_string(),
CommandHandlerType::PubSync(sync_handler),
);
result
} else {
get_error!("Internal error: sync handler not found", "CommandStatus")
}
}
CommandHandlerType::PubAsync(async_handler) => {
let cloned_handler = async_handler.box_clone();
match app
.spawn_async_command(command.to_string(), cloned_handler)
.await
{
Ok(_) => {
get_info!(
&format!("Async command '{}' started in the background", cmd_name),
"CommandStatus"
)
}
Err(e) => {
get_error!(
&format!("Failed to spawn async command: {}", e),
"CommandStatus"
)
}
}
}
}
} else if let Some(ref handler) = app.async_unknown_command_handler {
handler(command).await
} else if let Some(ref handler) = app.unknown_command_handler {
handler(command)
} else {
get_warn!(
&format!("Command not found or registered: '{}'", command),
"CommandStatus"
)
}
}