1use crate::{cli::state::DebuggerHelper, error::Result};
2use std::collections::HashSet;
3use strsim::levenshtein;
4
5#[derive(Debug, Clone)]
6pub struct Command {
7 pub name: &'static str,
8 pub aliases: &'static [&'static str],
9 pub help: &'static str,
10}
11
12pub struct Commands {
13 pub tx: Command,
14 pub reset: Command,
15 pub continue_: Command,
16 pub step: Command,
17 pub breakpoint: Command,
18 pub registers: Command,
19 pub memory: Command,
20 pub quit: Command,
21 pub help: Command,
22}
23
24impl Commands {
25 pub const fn new() -> Self {
26 Self {
27 tx: Command {
28 name: "start_tx",
29 aliases: &["n", "tx", "new_tx"],
30 help: "Start a new transaction",
31 },
32 reset: Command {
33 name: "reset",
34 aliases: &[],
35 help: "Reset debugger state",
36 },
37 continue_: Command {
38 name: "continue",
39 aliases: &["c"],
40 help: "Continue execution",
41 },
42 step: Command {
43 name: "step",
44 aliases: &["s"],
45 help: "Step execution",
46 },
47 breakpoint: Command {
48 name: "breakpoint",
49 aliases: &["b"],
50 help: "Set breakpoint",
51 },
52 registers: Command {
53 name: "register",
54 aliases: &["r", "reg", "registers"],
55 help: "View registers",
56 },
57 memory: Command {
58 name: "memory",
59 aliases: &["m", "mem"],
60 help: "View memory",
61 },
62 quit: Command {
63 name: "quit",
64 aliases: &["exit"],
65 help: "Exit debugger",
66 },
67 help: Command {
68 name: "help",
69 aliases: &["h", "?"],
70 help: "Show help for commands",
71 },
72 }
73 }
74
75 pub fn all_commands(&self) -> Vec<&Command> {
76 vec![
77 &self.tx,
78 &self.reset,
79 &self.continue_,
80 &self.step,
81 &self.breakpoint,
82 &self.registers,
83 &self.memory,
84 &self.quit,
85 &self.help,
86 ]
87 }
88
89 pub fn is_tx_command(&self, cmd: &str) -> bool {
90 self.tx.name == cmd || self.tx.aliases.contains(&cmd)
91 }
92
93 pub fn is_register_command(&self, cmd: &str) -> bool {
94 self.registers.name == cmd || self.registers.aliases.contains(&cmd)
95 }
96
97 pub fn is_quit_command(&self, cmd: &str) -> bool {
98 self.quit.name == cmd || self.quit.aliases.contains(&cmd)
99 }
100
101 pub fn is_help_command(&self, cmd: &str) -> bool {
102 self.help.name == cmd || self.help.aliases.contains(&cmd)
103 }
104
105 pub fn find_command(&self, name: &str) -> Option<&Command> {
106 self.all_commands()
107 .into_iter()
108 .find(|cmd| cmd.name == name || cmd.aliases.contains(&name))
109 }
110
111 pub fn get_all_command_strings(&self) -> HashSet<&'static str> {
113 let mut commands = HashSet::new();
114 for cmd in self.all_commands() {
115 commands.insert(cmd.name);
116 commands.extend(cmd.aliases);
117 }
118 commands
119 }
120
121 pub fn find_closest(&self, unknown_cmd: &str) -> Option<&Command> {
123 self.all_commands()
124 .into_iter()
125 .flat_map(|cmd| {
126 std::iter::once((cmd, cmd.name))
127 .chain(cmd.aliases.iter().map(move |&alias| (cmd, alias)))
128 })
129 .map(|(cmd, name)| (cmd, levenshtein(unknown_cmd, name)))
130 .filter(|&(_, distance)| distance <= 2)
131 .min_by_key(|&(_, distance)| distance)
132 .map(|(cmd, _)| cmd)
133 }
134}
135
136pub async fn cmd_help(helper: &DebuggerHelper, args: &[String]) -> Result<()> {
138 if args.len() > 1 {
139 if let Some(cmd) = helper.commands.find_command(&args[1]) {
141 println!("{} - {}", cmd.name, cmd.help);
142 if !cmd.aliases.is_empty() {
143 println!("Aliases: {}", cmd.aliases.join(", "));
144 }
145 return Ok(());
146 }
147 println!("Unknown command: '{}'", args[1]);
148 }
149
150 println!("Available commands:");
151 for cmd in helper.commands.all_commands() {
152 println!(" {:<12} - {}", cmd.name, cmd.help);
153 if !cmd.aliases.is_empty() {
154 println!(" aliases: {}", cmd.aliases.join(", "));
155 }
156 }
157 Ok(())
158}
159
160pub fn parse_int(s: &str) -> Option<usize> {
192 let (s, radix) = s.strip_prefix("0x").map_or((s, 10), |s| (s, 16));
193 usize::from_str_radix(&s.replace('_', ""), radix).ok()
194}