1use clap::Command;
2
3const BOLD: &str = "\x1b[1m";
4const RESET: &str = "\x1b[0m";
5const YELLOW: &str = "\x1b[33m";
6const GREEN: &str = "\x1b[32m";
7const CYAN: &str = "\x1b[36m";
8
9pub struct Printer {
10 cmd: Command,
11}
12
13impl Printer {
14 pub fn new(cmd: Command) -> Self {
15 Self { cmd }
16 }
17
18 pub fn print_help(&mut self) {
20 let bin_name = self.cmd.get_bin_name().unwrap_or("hexz").to_string();
21
22 println!(
23 "{}Usage:{} {}{}{} {}[OPTIONS]{} {}COMMAND{}",
24 BOLD, RESET, GREEN, bin_name, RESET, CYAN, RESET, YELLOW, RESET
25 );
26 println!();
27 if let Some(about) = self.cmd.get_about() {
28 println!("{}", about);
29 }
30 println!();
31
32 let mut archive_cmds = Vec::new();
33 let mut vm_cmds = Vec::new();
34 let mut sys_cmds = Vec::new();
35 let mut other_cmds = Vec::new();
36
37 let subcommands: Vec<Command> = self.cmd.get_subcommands().cloned().collect();
38
39 for sub in subcommands {
40 let name = sub.get_name().to_string();
41 if name == "help" {
42 continue;
43 }
44
45 let about = sub.get_about().map(|a| a.to_string()).unwrap_or_default();
46 let item = (name.clone(), about);
47
48 match name.as_str() {
49 "pack" | "inspect" | "diff" | "ls" | "build" | "analyze" | "convert" => {
50 archive_cmds.push(item)
51 }
52 "boot" | "install" | "snap" | "commit" | "mount" | "unmount" => vm_cmds.push(item),
53 "doctor" | "bench" | "serve" | "keygen" | "sign" | "verify" | "overlay" => {
54 sys_cmds.push(item)
55 }
56 _ => other_cmds.push(item),
57 }
58 }
59
60 self.print_section("Archive Operations", archive_cmds);
61 self.print_section("Virtual Machine Operations", vm_cmds);
62 self.print_section("System & Diagnostics", sys_cmds);
63
64 if !other_cmds.is_empty() {
65 self.print_section("Other Commands", other_cmds);
66 }
67
68 println!("{}Options:{}", BOLD, RESET);
69 println!(" {}{:<15}{} Print help", GREEN, "-h, --help", RESET);
70 println!(" {}{:<15}{} Print version", GREEN, "-V, --version", RESET);
71 println!();
72 println!(
73 "Run '{}{}{} COMMAND --help{}' for more information on a command.",
74 BOLD, YELLOW, bin_name, RESET
75 );
76 }
77
78 fn print_section(&self, header: &str, cmds: Vec<(String, String)>) {
79 if cmds.is_empty() {
80 return;
81 }
82
83 println!("{}{}{}:{}", BOLD, YELLOW, header, RESET);
84
85 for (name, about) in cmds {
86 println!(" {}{:<12}{} {}", GREEN, name, RESET, about);
87 }
88 println!();
89 }
90
91 pub fn print_subcommand_help(&mut self, sub_name: &str) {
93 let sub = match self.cmd.find_subcommand(sub_name) {
94 Some(s) => s,
95 None => return,
96 };
97
98 let bin_name = self.cmd.get_bin_name().unwrap_or("hexz");
99
100 println!(
102 "{}Usage:{} {} {} {} {} {}[OPTIONS] [ARGS]{}",
103 BOLD, RESET, GREEN, bin_name, sub_name, RESET, CYAN, RESET
104 );
105 println!();
106
107 if let Some(about) = sub.get_long_about().or_else(|| sub.get_about()) {
109 println!("{}", about);
110 }
111 println!();
112
113 let args: Vec<_> = sub.get_arguments().collect();
115
116 let (mut positionals, mut flags): (Vec<_>, Vec<_>) = args
119 .into_iter()
120 .filter(|a| a.get_id() != "help" && a.get_id() != "version")
121 .partition(|a| a.get_short().is_none() && a.get_long().is_none());
122
123 positionals.sort_by_key(|a| a.get_index().unwrap_or(usize::MAX));
126
127 flags.sort_by(|a, b| a.get_id().cmp(b.get_id()));
129
130 if !positionals.is_empty() {
132 println!("{}Arguments:{}", BOLD, RESET);
133 for arg in positionals {
134 let name = arg.get_id().as_str().to_uppercase();
135 let help = arg.get_help().map(|h| h.to_string()).unwrap_or_default();
136
137 let required_note = if arg.is_required_set() {
139 format!("{} (required){}", YELLOW, RESET)
140 } else {
141 String::new()
142 };
143
144 println!(" {}{:<28}{} {}{}", GREEN, name, RESET, help, required_note);
145 }
146 println!();
147 }
148
149 println!("{}Options:{}", BOLD, RESET);
151
152 for arg in flags {
153 let short = arg
154 .get_short()
155 .map(|s| format!("-{},", s))
156 .unwrap_or_default();
157 let long = arg
158 .get_long()
159 .map(|l| format!("--{}", l))
160 .unwrap_or_default();
161
162 let value = if arg.get_action().takes_values() {
164 let val_name = arg
165 .get_value_names()
166 .and_then(|names| names.first())
167 .map(|s| s.to_string())
168 .unwrap_or_else(|| "VAL".to_string());
169 format!(" <{}>", val_name.to_uppercase())
170 } else {
171 String::new()
172 };
173
174 let flag_str = format!("{} {}{}", short, long, value);
175 let help_text = arg.get_help().map(|h| h.to_string()).unwrap_or_default();
176
177 let required_note = if arg.is_required_set() {
178 format!("{} (required){}", YELLOW, RESET)
179 } else {
180 String::new()
181 };
182
183 println!(
184 " {}{:<28}{} {}{}",
185 GREEN,
186 flag_str.trim(),
187 RESET,
188 help_text,
189 required_note
190 );
191 }
192
193 println!(" {}{:<28}{} Print help", GREEN, "-h, --help", RESET);
195 println!();
196
197 println!("{}Example:{}", BOLD, RESET);
199 if let Some(example) = sub.get_after_help() {
200 println!(" {}", example);
201 } else {
202 println!(" {} {} [flags] [args]", bin_name, sub_name);
203 }
204 println!();
205 }
206}