chisel/
cmd.rs

1use crate::prelude::CHISEL_CHAR;
2use alloy_primitives::Address;
3use clap::{CommandFactory, Parser};
4use itertools::Itertools;
5use yansi::Paint;
6
7/// Chisel REPL commands.
8#[derive(Debug, Parser)]
9#[command(disable_help_flag = true, disable_help_subcommand = true)]
10pub enum ChiselCommand {
11    /// Display all commands.
12    #[command(visible_alias = "h", next_help_heading = "General")]
13    Help,
14
15    /// Quit the REPL.
16    #[command(visible_alias = "q")]
17    Quit,
18
19    /// Executes a shell command.
20    #[command(visible_alias = "e")]
21    Exec {
22        /// Command to execute.
23        command: String,
24        /// Command arguments.
25        #[arg(trailing_var_arg = true)]
26        args: Vec<String>,
27    },
28
29    /// Clear the current session source.
30    #[command(visible_alias = "c", next_help_heading = "Session")]
31    Clear,
32
33    /// Print the generated source contract.
34    #[command(visible_alias = "so")]
35    Source,
36
37    /// Save the current session to the cache.
38    #[command(visible_alias = "s")]
39    Save {
40        /// Optional session ID.
41        id: Option<String>,
42    },
43
44    /// Load a previous session from cache.
45    /// WARNING: This will overwrite the current session (though the current session will be
46    /// optimistically cached).
47    #[command(visible_alias = "l")]
48    Load {
49        /// Session ID to load.
50        id: String,
51    },
52
53    /// List all cached sessions.
54    #[command(name = "list", visible_alias = "ls")]
55    ListSessions,
56
57    /// Clear the cache of all stored sessions.
58    #[command(name = "clearcache", visible_alias = "cc")]
59    ClearCache,
60
61    /// Export the current REPL session source to a Script file.
62    #[command(visible_alias = "ex")]
63    Export,
64
65    /// Fetch an interface of a verified contract on Etherscan.
66    #[command(visible_alias = "fe")]
67    Fetch {
68        /// Contract address.
69        addr: Address,
70        /// Interface name.
71        name: String,
72    },
73
74    /// Open the current session in an editor.
75    Edit,
76
77    /// Fork an RPC in the current session.
78    #[command(visible_alias = "f", next_help_heading = "Environment")]
79    Fork {
80        /// Fork URL, environment variable, or RPC endpoints alias (empty to return to local
81        /// network).
82        url: Option<String>,
83    },
84
85    /// Enable / disable traces for the current session.
86    #[command(visible_alias = "t")]
87    Traces,
88
89    /// Set calldata (`msg.data`) for the current session (appended after function selector). Clears
90    /// it if no argument provided.
91    #[command(visible_alias = "cd")]
92    Calldata {
93        /// Calldata (empty to clear).
94        data: Option<String>,
95    },
96
97    /// Dump the raw memory.
98    #[command(name = "memdump", visible_alias = "md", next_help_heading = "Debug")]
99    MemDump,
100
101    /// Dump the raw stack.
102    #[command(name = "stackdump", visible_alias = "sd")]
103    StackDump,
104
105    /// Display the raw value of a variable's stack allocation. For variables that are > 32 bytes in
106    /// length, this will display their memory pointer.
107    #[command(name = "rawstack", visible_alias = "rs")]
108    RawStack {
109        /// Variable name.
110        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}