1use colored::Colorize;
4
5struct CommandEntry {
6 command: &'static str, summary: &'static str, description: &'static str, usage: &'static str, requires_auth: bool, requires_admin: bool, params: &'static [(&'static str, &'static str)], }
14
15const COMMANDS: &[CommandEntry] = &[
16 CommandEntry {
18 command: "help",
19 summary: "Lists all available commands.",
20 description: "Prints a summary of every available command, grouped by namespace.",
21 usage: "help",
22 requires_auth: false,
23 requires_admin: false,
24 params: &[],
25 },
26 CommandEntry {
27 command: "help <command>",
28 summary: "Shows detailed help for a specific command.",
29 description: "Prints the full description, usage, parameters, and auth requirement for the given command.",
30 usage: "help server:status",
31 requires_auth: false,
32 requires_admin: false,
33 params: &[("command", "The command to look up, e.g. server:status")],
34 },
35 CommandEntry {
37 command: "server:login",
38 summary: "Authenticates an operator session in the terminal.",
39 description: "Prompts for a username and password and verifies them against the database. On success, grants access to write/destructive commands for the next 20 minutes of inactivity.",
40 usage: "server:login",
41 requires_auth: false,
42 requires_admin: false,
43 params: &[],
44 },
45 CommandEntry {
46 command: "server:status",
47 summary: "Shows uptime and basic server info.",
48 description: "Prints how long the server has been running, what address it is listening on, and whether testing mode is active.",
49 usage: "server:status",
50 requires_auth: false,
51 requires_admin: false,
52 params: &[],
53 },
54 CommandEntry {
55 command: "server:logout",
56 summary: "Ends the current operator session.",
57 description: "Clears the authenticated session immediately. Any subsequent write commands will require logging in again.",
58 usage: "server:logout",
59 requires_auth: true,
60 requires_admin: false,
61 params: &[],
62 },
63 CommandEntry {
64 command: "server:shutdown",
65 summary: "Gracefully shuts the server down.",
66 description: "Signals the server to stop accepting new requests, waits for in-flight requests to complete, flushes logs, and exits cleanly.",
67 usage: "server:shutdown",
68 requires_auth: true,
69 requires_admin: true,
70 params: &[],
71 },
72 CommandEntry {
73 command: "server:logs",
74 summary: "Displays recent server logs.",
75 description: "Prints logs in order from most recent to oldest, allowing for optional filter for last X days.",
76 usage: "server:logs [days]",
77 requires_auth: false,
78 requires_admin: false,
79 params: &[("days", "Optional number of days to display logs for")],
80 },
81 CommandEntry {
82 command: "server:audit",
83 summary: "Displays recent audit logs.",
84 description: "Prints audit logs in order from most recent to oldest, allowing for optional filter for last X days.",
85 usage: "server:audit [days]",
86 requires_auth: false,
87 requires_admin: false,
88 params: &[("days", "Optional number of days to display audit logs for")],
89 },
90 CommandEntry {
92 command: "db:stats",
93 summary: "Displays database statistics.",
94 description: "Prints the number of rows in each table, total row count, and estimated size of each table and database.",
95 usage: "db:stats",
96 requires_auth: false,
97 requires_admin: false,
98 params: &[],
99 },
100 CommandEntry {
101 command: "db:table",
102 summary: "Displays records from a specific table.",
103 description: "Prints the records from the specified table, with an option to view a specific page of results.",
104 usage: "db:table <name>, <page>",
105 requires_auth: false,
106 requires_admin: false,
107 params: &[
108 ("name", "The name of the table to display"),
109 ("page", "The page of results to display"),
110 ],
111 },
112];
113
114pub fn print_all() {
118 println!();
119 println!("{}", " Available Commands".cyan().bold());
120 println!(
121 "{}",
122 " ─────────────────────────────────────────────────────".dimmed()
123 );
124
125 let namespaces = ["help", "server", "db", "user", "test"];
126
127 for ns in namespaces {
128 println!();
129 println!(" {}", ns.white().bold());
130
131 for entry in COMMANDS {
132 let ns_prefix = format!("{}:", ns);
133 let belongs = entry.command == ns
134 || entry.command.starts_with(&ns_prefix)
135 || (ns == "help" && entry.command.starts_with("help "));
136
137 if belongs {
138 let auth_marker = if entry.requires_auth {
139 " *".yellow().to_string()
140 } else {
141 String::new()
142 };
143 println!(
144 " {:<30} {}{}",
145 entry.command.green(),
146 entry.summary.dimmed(),
147 auth_marker
148 );
149 }
150 }
151 }
152
153 println!();
154 println!(
155 " {} {}",
156 "*".yellow(),
157 "marked commands require server:login before use.".dimmed()
158 );
159 println!(
160 " {}",
161 "Run `help <command>` for detailed usage on any command.".dimmed()
162 );
163 println!();
164}
165
166pub fn print_command(command: &str) {
170 let entry = COMMANDS.iter().find(|e| {
171 e.command == command
172 || e.command == format!("server:{}", command)
173 || e.command == format!("help {}", command)
174 });
175
176 match entry {
177 None => {
178 println!("{} {}", "No help entry found for:".red(), command.white());
179 println!("{}", "Run `help` to see all available commands.".dimmed());
180 }
181 Some(e) => {
182 println!();
183 println!(" {}", e.command.cyan().bold());
184 println!(
185 "{}",
186 " ─────────────────────────────────────────────────────".dimmed()
187 );
188 println!(
189 " {:<16} {}",
190 "Description:".white(),
191 e.description.dimmed()
192 );
193 println!(" {:<16} {}", "Usage:".white(), e.usage.green());
194 println!(
195 " {:<16} {}",
196 "Auth required:".white(),
197 if e.requires_auth {
198 "yes".yellow().to_string()
199 } else {
200 "no".dimmed().to_string()
201 }
202 );
203
204 println!(
205 " {:<16} {}",
206 "Admin permissions required:".white(),
207 if e.requires_admin {
208 "yes".yellow().to_string()
209 } else {
210 "no".dimmed().to_string()
211 }
212 );
213
214 if !e.params.is_empty() {
215 println!(" {}", "Parameters:".white());
216 for (name, desc) in e.params {
217 println!(" {:<16} {}", name.green(), desc.dimmed());
218 }
219 }
220
221 println!();
222 }
223 }
224}