use crate::client::TrustyCommand;
use super::events::{SlashCommand, parse_slash};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Dispatch {
Command(TrustyCommand),
Hint(String),
Echo,
}
pub fn route(line: &str, focused: Option<&str>) -> Dispatch {
let trimmed = line.trim();
if trimmed.is_empty() {
return Dispatch::Echo;
}
match parse_slash(trimmed) {
Some(cmd) => route_slash(cmd, trimmed, focused),
None => match focused {
Some(target) => Dispatch::Command(TrustyCommand::ManagedSend {
target: target.to_string(),
text: trimmed.to_string(),
}),
None => Dispatch::Echo,
},
}
}
fn route_slash(cmd: SlashCommand, line: &str, focused: Option<&str>) -> Dispatch {
let args = line
.split_whitespace()
.skip(1)
.collect::<Vec<_>>()
.join(" ");
let args = args.trim();
match cmd {
SlashCommand::Help => Dispatch::Command(TrustyCommand::Help),
SlashCommand::Health => Dispatch::Command(TrustyCommand::Health),
SlashCommand::Sessions => Dispatch::Command(TrustyCommand::ManagedList),
SlashCommand::New => route_new(args),
SlashCommand::Attach => target_only(args, focused, |t| TrustyCommand::ManagedAttachCmd {
target: t,
}),
SlashCommand::Stop => target_only(args, focused, |t| TrustyCommand::ManagedRuntimeStop {
target: t,
}),
SlashCommand::Resume => target_only(args, focused, |t| TrustyCommand::ManagedResume {
target: t,
}),
SlashCommand::Kill => target_only(args, focused, |t| TrustyCommand::ManagedDecommission {
target: t,
}),
SlashCommand::Activity => target_only(args, focused, |t| TrustyCommand::ManagedActivity {
target: t,
}),
SlashCommand::Get => {
target_only(args, focused, |t| TrustyCommand::ManagedGet { target: t })
}
SlashCommand::Send => route_target_and_text(args, focused, "send", |target, text| {
TrustyCommand::ManagedSend { target, text }
}),
SlashCommand::Answer => route_target_and_text(args, focused, "answer", |target, answer| {
TrustyCommand::ManagedAnswer { target, answer }
}),
SlashCommand::Unknown(word) => {
Dispatch::Hint(format!("unknown command: /{word} — try /help"))
}
}
}
fn target_only(
args: &str,
focused: Option<&str>,
build: impl FnOnce(String) -> TrustyCommand,
) -> Dispatch {
match resolve_target(args, focused) {
Some(target) => Dispatch::Command(build(target)),
None => {
Dispatch::Hint("no session selected — focus a session or pass an id/name".to_string())
}
}
}
fn route_target_and_text(
args: &str,
focused: Option<&str>,
verb: &str,
build: impl FnOnce(String, String) -> TrustyCommand,
) -> Dispatch {
let (target, text) = match focused {
Some(t) => (t.to_string(), args.to_string()),
None => {
let mut parts = args.splitn(2, char::is_whitespace);
let target = parts.next().unwrap_or("").trim().to_string();
let text = parts.next().unwrap_or("").trim().to_string();
(target, text)
}
};
if target.is_empty() {
return Dispatch::Hint(format!(
"{verb}: no session selected — focus a session or pass an id/name"
));
}
if text.trim().is_empty() {
return Dispatch::Hint(format!("{verb}: text is required"));
}
Dispatch::Command(build(target, text.trim().to_string()))
}
fn route_new(args: &str) -> Dispatch {
let mut parts = args.splitn(3, char::is_whitespace);
let repo = parts.next().unwrap_or("").trim();
let git_ref = parts.next().unwrap_or("").trim();
let task = parts.next().unwrap_or("").trim();
if repo.is_empty() || git_ref.is_empty() || task.is_empty() {
return Dispatch::Hint("new: usage /new <repo> <ref> <task>".to_string());
}
Dispatch::Command(TrustyCommand::ManagedNew {
repo_url: repo.to_string(),
git_ref: git_ref.to_string(),
task: task.to_string(),
name_hint: None,
runtime: None,
inject_task: None,
deliverable_id: None,
force_new: false,
})
}
fn resolve_target(args: &str, focused: Option<&str>) -> Option<String> {
args.split_whitespace()
.next()
.map(str::to_string)
.or_else(|| focused.map(str::to_string))
}