use crate::executor::execute_command;
use crate::types::CommandDef;
use anyhow::{Context, Result, bail};
use regex::Regex;
use std::{
collections::HashMap,
io::{Read, Write},
path::Path,
process::{Command as ProcessCommand, Stdio},
};
pub fn choose_command<'a>(
commands_vec: &'a [CommandDef],
config_dir: &Path,
filter_cmd: &str,
initial_query: Option<&str>,
) -> Result<&'a CommandDef> {
if commands_vec.is_empty() {
bail!(
"No command snippets defined. Looked in: {}",
config_dir.display()
);
}
let mut choice_map: HashMap<String, &CommandDef> = HashMap::new();
let prefix = "\x1b[33m";
let suffix = "\x1b[0m";
let mut colored_lines = Vec::new();
for cmd_def in commands_vec.iter() {
let tags_str = if cmd_def.tags.is_empty() {
String::new()
} else {
cmd_def
.tags
.iter()
.map(|t| format!("#{}", t))
.collect::<Vec<_>>()
.join(" ")
};
let raw_line = if tags_str.is_empty() {
cmd_def.description.clone()
} else {
format!("{} {}", cmd_def.description, tags_str)
};
let colored_line = if tags_str.is_empty() {
cmd_def.description.clone()
} else {
format!("{} {}{}{}", cmd_def.description, prefix, tags_str, suffix)
};
choice_map.insert(raw_line.clone(), cmd_def);
colored_lines.push(colored_line);
}
let mut parts = filter_cmd.split_whitespace();
let filter_prog = parts.next().unwrap();
let mut effective_args: Vec<String> = parts.map(|s| s.to_string()).collect();
if let Some(query) = initial_query {
match filter_prog {
"fzf" => {
effective_args.push("--query".to_string());
effective_args.push(query.to_string());
}
prog if prog == "gum"
&& effective_args
.first()
.map(|s| s == "filter")
.unwrap_or(false) =>
{
effective_args.push("--filter".to_string());
effective_args.push(query.to_string());
}
_ => {}
}
}
let mut filter_child = ProcessCommand::new(filter_prog)
.args(&effective_args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.with_context(|| format!("Failed to spawn filter command '{}'", filter_cmd))?;
{
let mut stdin = filter_child
.stdin
.take()
.context("Failed to open filter stdin")?;
for line in &colored_lines {
writeln!(stdin, "{}", line).context("Failed to write to filter stdin")?;
}
}
let mut selected = String::new();
{
let mut stdout = filter_child
.stdout
.take()
.context("Failed to open filter stdout")?;
stdout
.read_to_string(&mut selected)
.context("Failed to read filter output")?;
}
let status = filter_child
.wait()
.context("Failed to wait for filter process")?;
if !status.success() {
bail!("No selection made. Exiting.");
}
let key = Regex::new(r"\x1b\[[0-9;]*m")
.unwrap()
.replace_all(selected.trim(), "")
.to_string();
choice_map
.get(&key)
.copied()
.with_context(|| format!("Selected command '{}' not found", key))
}
pub fn select_and_execute_command(
commands_vec: &[CommandDef],
config_dir: &Path,
filter_cmd: &str,
initial_query: Option<&str>,
) -> Result<()> {
let cmd_def = choose_command(commands_vec, config_dir, filter_cmd, initial_query)?;
execute_command(cmd_def).with_context(|| {
format!(
"Failed to execute command snippet '{}'",
cmd_def.description
)
})
}