mermaid_cli/domain/
slash_commands.rs1#[derive(Debug, Clone, Copy)]
12pub struct SlashCommand {
13 pub name: &'static str,
15 pub aliases: &'static [&'static str],
18 pub description: &'static str,
20 pub arg_hint: Option<&'static str>,
23}
24
25pub const COMMAND_REGISTRY: &[SlashCommand] = &[
28 SlashCommand {
29 name: "model",
30 aliases: &[],
31 description: "Switch model (auto-pulls if needed) or show current",
32 arg_hint: Some("[name]"),
33 },
34 SlashCommand {
35 name: "reasoning",
36 aliases: &[],
37 description: "Set reasoning depth (none, minimal, low, medium, high, max, xhigh)",
38 arg_hint: Some("[level]"),
39 },
40 SlashCommand {
41 name: "clear",
42 aliases: &[],
43 description: "Clear chat history",
44 arg_hint: None,
45 },
46 SlashCommand {
47 name: "save",
48 aliases: &[],
49 description: "Save current conversation",
50 arg_hint: Some("[name]"),
51 },
52 SlashCommand {
53 name: "load",
54 aliases: &[],
55 description: "Load a conversation",
56 arg_hint: Some("[name]"),
57 },
58 SlashCommand {
59 name: "list",
60 aliases: &[],
61 description: "List saved conversations",
62 arg_hint: None,
63 },
64 SlashCommand {
65 name: "usage",
66 aliases: &[],
67 description: "Show provider token usage and session totals",
68 arg_hint: None,
69 },
70 SlashCommand {
71 name: "context",
72 aliases: &[],
73 description: "Show current context-window estimate and prompt budget",
74 arg_hint: None,
75 },
76 SlashCommand {
77 name: "compact",
78 aliases: &["compress", "summarize"],
79 description: "Compact conversation context with optional focus instructions",
80 arg_hint: Some("[instructions]"),
81 },
82 SlashCommand {
83 name: "cloud-setup",
84 aliases: &[],
85 description: "Configure Ollama Cloud API key",
86 arg_hint: None,
87 },
88 SlashCommand {
89 name: "help",
90 aliases: &["h"],
91 description: "Show command help",
92 arg_hint: None,
93 },
94 SlashCommand {
95 name: "quit",
96 aliases: &["q"],
97 description: "Quit the application",
98 arg_hint: None,
99 },
100];
101
102pub fn filter_by_prefix(typed: &str) -> Vec<&'static SlashCommand> {
107 let needle = typed.to_lowercase();
108 if needle.is_empty() {
109 return COMMAND_REGISTRY.iter().collect();
110 }
111 COMMAND_REGISTRY
112 .iter()
113 .filter(|cmd| {
114 cmd.name.starts_with(&needle) || cmd.aliases.iter().any(|a| a.starts_with(&needle))
115 })
116 .collect()
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 #[test]
124 fn filter_by_prefix_empty_returns_all() {
125 let result = filter_by_prefix("");
126 assert_eq!(result.len(), COMMAND_REGISTRY.len());
127 assert_eq!(result[0].name, COMMAND_REGISTRY[0].name);
129 }
130
131 #[test]
132 fn filter_by_prefix_exact_match() {
133 let result = filter_by_prefix("model");
134 assert_eq!(result.len(), 1);
135 assert_eq!(result[0].name, "model");
136 }
137
138 #[test]
139 fn filter_by_prefix_partial_prefix_matches_one() {
140 let result = filter_by_prefix("mod");
141 assert_eq!(result.len(), 1);
142 assert_eq!(result[0].name, "model");
143 }
144
145 #[test]
146 fn filter_by_prefix_no_match_returns_empty() {
147 let result = filter_by_prefix("zzzzz");
148 assert!(result.is_empty());
149 }
150
151 #[test]
152 fn filter_by_prefix_matches_aliases() {
153 let result = filter_by_prefix("q");
155 assert!(
156 result.iter().any(|c| c.name == "quit"),
157 "expected quit in: {:?}",
158 result.iter().map(|c| c.name).collect::<Vec<_>>()
159 );
160 }
161
162 #[test]
163 fn filter_by_prefix_is_case_insensitive() {
164 let upper = filter_by_prefix("MODEL");
166 assert_eq!(upper.len(), 1);
167 assert_eq!(upper[0].name, "model");
168 }
169
170 #[test]
171 fn registry_has_no_duplicate_names() {
172 let mut names: Vec<&str> = COMMAND_REGISTRY.iter().map(|c| c.name).collect();
176 names.sort_unstable();
177 let len_before = names.len();
178 names.dedup();
179 assert_eq!(
180 names.len(),
181 len_before,
182 "duplicate command name detected in COMMAND_REGISTRY"
183 );
184 }
185}