1use crate::prelude::CHISEL_CHAR;
2use alloy_primitives::Address;
3use clap::{CommandFactory, Parser};
4use itertools::Itertools;
5use yansi::Paint;
6
7#[derive(Debug, Parser)]
9#[command(disable_help_flag = true, disable_help_subcommand = true)]
10pub enum ChiselCommand {
11 #[command(visible_alias = "h", next_help_heading = "General")]
13 Help,
14
15 #[command(visible_alias = "q")]
17 Quit,
18
19 #[command(visible_alias = "e")]
21 Exec {
22 command: String,
24 #[arg(trailing_var_arg = true)]
26 args: Vec<String>,
27 },
28
29 #[command(visible_alias = "c", next_help_heading = "Session")]
31 Clear,
32
33 #[command(visible_alias = "so")]
35 Source,
36
37 #[command(visible_alias = "s")]
41 Save {
42 id: Option<String>,
44 },
45
46 #[command(visible_alias = "l")]
53 Load {
54 id: String,
56 },
57
58 #[command(name = "list", visible_alias = "ls")]
60 ListSessions,
61
62 #[command(name = "clearcache", visible_alias = "cc")]
64 ClearCache,
65
66 #[command(visible_alias = "ex")]
68 Export,
69
70 #[command(visible_alias = "fe")]
72 Fetch {
73 addr: Address,
75 name: String,
77 },
78
79 Edit,
81
82 #[command(visible_alias = "f", next_help_heading = "Environment")]
84 Fork {
85 url: Option<String>,
88 },
89
90 #[command(visible_alias = "t")]
92 Traces,
93
94 #[command(visible_alias = "cd")]
97 Calldata {
98 data: Option<String>,
100 },
101
102 #[command(name = "memdump", visible_alias = "md", next_help_heading = "Debug")]
104 MemDump,
105
106 #[command(name = "stackdump", visible_alias = "sd")]
108 StackDump,
109
110 #[command(name = "rawstack", visible_alias = "rs")]
113 RawStack {
114 var: String,
116 },
117}
118
119impl ChiselCommand {
120 pub fn parse(input: &str) -> eyre::Result<Self> {
121 let args = input.split_whitespace();
122 let args = std::iter::once("chisel").chain(args);
123 Self::try_parse_from(args)
124 .map_err(|e| eyre::eyre!("{}; for more information, see `!help`", e.kind()))
125 }
126
127 pub fn format_help() -> String {
128 let cmd = Self::command();
129 let mut categories = Vec::new();
130 let mut cat = None;
131 for sub in cmd.get_subcommands() {
132 if let Some(cat_) = sub.get_next_help_heading()
133 && Some(cat_) != cat
134 {
135 cat = Some(cat_);
136 categories.push((cat_, vec![]));
137 }
138 categories.last_mut().unwrap().1.push(sub);
139 }
140 format!(
141 "{}\n{}",
142 format!("{CHISEL_CHAR} Chisel help\n=============").cyan(),
143 categories
144 .iter()
145 .map(|(cat, cat_cmds)| {
146 format!(
147 "{}\n{}\n",
148 cat.magenta(),
149 cat_cmds
150 .iter()
151 .map(|&cmd| format!(
152 "\t{}{} - {}",
153 std::iter::once(cmd.get_name())
154 .chain(cmd.get_visible_aliases())
155 .map(|s| format!("!{}", s.green()))
156 .format(" | "),
157 {
158 let usage = get_usage(cmd);
159 if usage.is_empty() {
160 String::new()
161 } else {
162 format!(" {usage}")
163 }
164 }
165 .green(),
166 cmd.get_about().expect("command is missing about"),
167 ))
168 .format("\n")
169 )
170 })
171 .format("\n")
172 )
173 }
174}
175
176fn get_usage(cmd: &clap::Command) -> String {
177 let s = cmd.clone().render_usage().to_string();
178 if let Some(idx) = s.find(['[', '<']) {
179 return s[idx..].to_string();
180 }
181 String::new()
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn print_help() {
190 let _ = sh_eprintln!("{}", ChiselCommand::format_help());
191 }
192}