use super::dropdown::{self, DropdownItem, DropdownState};
use ratatui::text::Line;
#[derive(Clone, Debug)]
pub struct SlashCommand {
pub command: &'static str,
pub description: &'static str,
pub arg_hint: Option<&'static str>,
}
impl DropdownItem for SlashCommand {
fn label(&self) -> &str {
self.command
}
fn description(&self) -> String {
self.description.to_string()
}
fn matches_filter(&self, filter: &str) -> bool {
self.command.starts_with(filter)
}
}
pub fn from_input(
commands: &'static [(&'static str, &'static str, Option<&'static str>)],
input: &str,
) -> Option<DropdownState<SlashCommand>> {
let items: Vec<SlashCommand> = commands
.iter()
.map(|(cmd, desc, arg_hint)| SlashCommand {
command: cmd,
description: desc,
arg_hint: *arg_hint,
})
.collect();
let mut dd = DropdownState::new(items, "\u{1f43b} Commands");
if dd.apply_filter(input) {
Some(dd)
} else {
None
}
}
pub fn build_menu_lines(state: &DropdownState<SlashCommand>) -> Vec<Line<'static>> {
dropdown::build_dropdown_lines(state)
}
#[cfg(test)]
mod tests {
use super::*;
const TEST_COMMANDS: &[(&str, &str, Option<&str>)] = &[
("/agent", "Agents", Some("<name>")),
("/compact", "Compact", None),
("/diff", "Diff", None),
("/exit", "Quit", None),
("/expand", "Expand", None),
("/model", "Pick model", None),
];
#[test]
fn from_input_all() {
let state = from_input(TEST_COMMANDS, "/").unwrap();
assert_eq!(state.filtered.len(), 6);
}
#[test]
fn from_input_filtered() {
let state = from_input(TEST_COMMANDS, "/m").unwrap();
assert_eq!(state.filtered.len(), 1);
assert_eq!(state.filtered[0].command, "/model");
}
#[test]
fn from_input_no_match() {
assert!(from_input(TEST_COMMANDS, "/z").is_none());
}
#[test]
fn selected_command() {
let state = from_input(TEST_COMMANDS, "/").unwrap();
assert_eq!(state.selected_item().unwrap().command, "/agent");
}
}