1use std::collections::BTreeMap;
17
18const COMMANDS: &[(&str, &str)] = &[
20 ("abort", include_str!("help/abort.md")),
21 ("apply", include_str!("help/apply.md")),
22 ("apply-clusters", include_str!("help/apply-clusters.md")),
23 (
24 "apply-connections",
25 include_str!("help/apply-connections.md"),
26 ),
27 (
28 "apply-network-policies",
29 include_str!("help/apply-network-policies.md"),
30 ),
31 ("apply-roles", include_str!("help/apply-roles.md")),
32 ("apply-secrets", include_str!("help/apply-secrets.md")),
33 ("apply-sources", include_str!("help/apply-sources.md")),
34 ("apply-tables", include_str!("help/apply-tables.md")),
35 ("clean", include_str!("help/clean.md")),
36 ("compile", include_str!("help/compile.md")),
37 ("explain", include_str!("help/explain.md")),
38 ("init", include_str!("help/init.md")),
39 ("debug", include_str!("help/debug.md")),
40 ("delete", include_str!("help/delete.md")),
41 ("dev", include_str!("help/dev.md")),
42 ("promote", include_str!("help/promote.md")),
43 ("describe", include_str!("help/describe.md")),
44 ("list", include_str!("help/list.md")),
45 ("lock", include_str!("help/lock.md")),
46 ("lsp", include_str!("help/lsp.md")),
47 ("log", include_str!("help/log.md")),
48 ("mcp", include_str!("help/mcp.md")),
49 ("new", include_str!("help/new.md")),
50 ("profiles", include_str!("help/profiles.md")),
51 ("setup", include_str!("help/setup.md")),
52 ("sql", include_str!("help/sql.md")),
53 ("wait", include_str!("help/wait.md")),
54 ("stage", include_str!("help/stage.md")),
55 ("test", include_str!("help/test.md")),
56];
57
58const ALIASES: &[(&str, &str)] = &[
60 ("branches", "list"),
61 ("build", "compile"),
62 ("clusters", "apply-clusters"),
63 ("connections", "apply-connections"),
64 ("deployments", "list"),
65 ("gen-data-contracts", "lock"),
66 ("history", "log"),
67 ("network-policies", "apply-network-policies"),
68 ("deploy", "promote"),
69 ("profile", "profiles"),
70 ("ready", "wait"),
71 ("roles", "apply-roles"),
72 ("secrets", "apply-secrets"),
73 ("show", "describe"),
74 ("sources", "apply-sources"),
75 ("tables", "apply-tables"),
76];
77
78pub fn help_for(name: &str) -> Option<&'static str> {
83 let canonical = resolve_alias(name);
84 COMMANDS
85 .iter()
86 .find(|(cmd, _)| *cmd == canonical)
87 .map(|(_, text)| *text)
88}
89
90pub fn all_help() -> String {
95 let mut out = String::new();
96 for (i, (name, text)) in COMMANDS.iter().enumerate() {
97 if i > 0 {
98 out.push('\n');
99 }
100 out.push_str(&format!("--- mz-deploy help {name} ---\n\n"));
101 out.push_str(text);
102 if !text.ends_with('\n') {
103 out.push('\n');
104 }
105 }
106 out
107}
108
109#[allow(clippy::print_stderr)]
111pub fn print_unknown_command(name: &str) {
112 use owo_colors::{OwoColorize, Stream, Style};
113
114 let error_style = Style::new().bright_red().bold();
115 eprintln!(
116 "{}: no help entry for '{}'",
117 "error".if_supports_color(Stream::Stderr, |t| error_style.style(t)),
118 name
119 );
120 eprintln!();
121
122 let mut commands: BTreeMap<&str, Vec<&str>> = BTreeMap::new();
123 for (cmd, _) in COMMANDS {
124 commands.insert(cmd, vec![]);
125 }
126 for (alias, canonical) in ALIASES {
127 if let Some(aliases) = commands.get_mut(canonical) {
128 aliases.push(alias);
129 }
130 }
131
132 eprintln!("Available commands:");
133 for (cmd, aliases) in &commands {
134 if aliases.is_empty() {
135 eprintln!(" {cmd}");
136 } else {
137 eprintln!(" {cmd} ({})", aliases.join(", "));
138 }
139 }
140}
141
142fn resolve_alias(name: &str) -> &str {
143 ALIASES
144 .iter()
145 .find(|(alias, _)| *alias == name)
146 .map(|(_, canonical)| *canonical)
147 .unwrap_or(name)
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[mz_ore::test]
155 fn help_for_canonical_name() {
156 assert!(help_for("compile").is_some());
157 assert!(help_for("stage").is_some());
158 assert!(help_for("apply").is_some());
159 assert!(help_for("promote").is_some());
160 }
161
162 #[mz_ore::test]
163 fn help_for_alias() {
164 assert_eq!(help_for("build"), help_for("compile"));
165 assert_eq!(help_for("show"), help_for("describe"));
166 assert_eq!(help_for("history"), help_for("log"));
167 assert_eq!(help_for("deployments"), help_for("list"));
168 assert_eq!(help_for("branches"), help_for("list"));
169 assert_eq!(help_for("gen-data-contracts"), help_for("lock"));
170 assert_eq!(help_for("ready"), help_for("wait"));
171 assert_eq!(help_for("clusters"), help_for("apply-clusters"));
172 assert_eq!(help_for("roles"), help_for("apply-roles"));
173 assert_eq!(help_for("deploy"), help_for("promote"));
174 }
175
176 #[mz_ore::test]
177 fn help_for_unknown() {
178 assert!(help_for("nonexistent").is_none());
179 }
180
181 #[mz_ore::test]
182 fn all_help_contains_all_commands() {
183 let all = all_help();
184 for (name, _) in COMMANDS {
185 assert!(
186 all.contains(&format!("--- mz-deploy help {name} ---")),
187 "missing delimiter for {name}"
188 );
189 }
190 }
191}