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")]
39 Save {
40 id: Option<String>,
42 },
43
44 #[command(visible_alias = "l")]
48 Load {
49 id: String,
51 },
52
53 #[command(name = "list", visible_alias = "ls")]
55 ListSessions,
56
57 #[command(name = "clearcache", visible_alias = "cc")]
59 ClearCache,
60
61 #[command(visible_alias = "ex")]
63 Export,
64
65 #[command(visible_alias = "fe")]
67 Fetch {
68 addr: Address,
70 name: String,
72 },
73
74 Edit,
76
77 #[command(visible_alias = "f", next_help_heading = "Environment")]
79 Fork {
80 url: Option<String>,
83 },
84
85 #[command(visible_alias = "t")]
87 Traces,
88
89 #[command(visible_alias = "cd")]
92 Calldata {
93 data: Option<String>,
95 },
96
97 #[command(name = "memdump", visible_alias = "md", next_help_heading = "Debug")]
99 MemDump,
100
101 #[command(name = "stackdump", visible_alias = "sd")]
103 StackDump,
104
105 #[command(name = "rawstack", visible_alias = "rs")]
108 RawStack {
109 var: String,
111 },
112}
113
114impl ChiselCommand {
115 pub fn parse(input: &str) -> eyre::Result<Self> {
116 let args = input.split_whitespace();
117 let args = std::iter::once("chisel").chain(args);
118 Self::try_parse_from(args)
119 .map_err(|e| eyre::eyre!("{}; for more information, see `!help`", e.kind()))
120 }
121
122 pub fn format_help() -> String {
123 let cmd = Self::command();
124 let mut categories = Vec::new();
125 let mut cat = None;
126 for sub in cmd.get_subcommands() {
127 if let Some(cat_) = sub.get_next_help_heading()
128 && Some(cat_) != cat
129 {
130 cat = Some(cat_);
131 categories.push((cat_, vec![]));
132 }
133 categories.last_mut().unwrap().1.push(sub);
134 }
135 format!(
136 "{}\n{}",
137 format!("{CHISEL_CHAR} Chisel help\n=============").cyan(),
138 categories
139 .iter()
140 .map(|(cat, cat_cmds)| {
141 format!(
142 "{}\n{}\n",
143 cat.magenta(),
144 cat_cmds
145 .iter()
146 .map(|&cmd| format!(
147 "\t{}{} - {}",
148 std::iter::once(cmd.get_name())
149 .chain(cmd.get_visible_aliases())
150 .map(|s| format!("!{}", s.green()))
151 .format(" | "),
152 {
153 let usage = get_usage(cmd);
154 if usage.is_empty() {
155 String::new()
156 } else {
157 format!(" {usage}")
158 }
159 }
160 .green(),
161 cmd.get_about().expect("command is missing about"),
162 ))
163 .format("\n")
164 )
165 })
166 .format("\n")
167 )
168 }
169}
170
171fn get_usage(cmd: &clap::Command) -> String {
172 let s = cmd.clone().render_usage().to_string();
173 if let Some(idx) = s.find(['[', '<']) {
174 return s[idx..].to_string();
175 }
176 String::new()
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn print_help() {
185 let _ = sh_eprintln!("{}", ChiselCommand::format_help());
186 }
187}