spydecy_debugger/
commands.rs1use std::fmt;
6
7#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum Command {
10 Step,
12 Continue,
14 Visualize,
16 Inspect(String),
18 Break(Breakpoint),
20 ListBreakpoints,
22 ClearBreakpoint(usize),
24 Help,
26 Quit,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum Breakpoint {
33 BoundaryElimination,
35 Phase(String),
37 Function(String),
39}
40
41impl fmt::Display for Breakpoint {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 match self {
44 Self::BoundaryElimination => write!(f, "Boundary Elimination"),
45 Self::Phase(phase) => write!(f, "Phase: {phase}"),
46 Self::Function(func) => write!(f, "Function: {func}"),
47 }
48 }
49}
50
51pub fn parse_command(input: &str) -> Result<Command, String> {
57 let input = input.trim();
58
59 if input.is_empty() {
60 return Ok(Command::Step); }
62
63 let parts: Vec<&str> = input.split_whitespace().collect();
64
65 match parts[0] {
66 "step" | "s" => Ok(Command::Step),
67 "continue" | "c" => Ok(Command::Continue),
68 "visualize" | "v" => Ok(Command::Visualize),
69 "inspect" | "i" => {
70 if parts.len() < 2 {
71 Err("inspect requires a target".to_owned())
72 } else {
73 Ok(Command::Inspect(parts[1..].join(" ")))
74 }
75 }
76 "break" | "b" => {
77 if parts.len() < 2 {
78 Err("break requires a breakpoint type".to_owned())
79 } else {
80 parse_breakpoint(&parts[1..])
81 }
82 }
83 "list" | "l" => Ok(Command::ListBreakpoints),
84 "clear" => {
85 if parts.len() < 2 {
86 Err("clear requires breakpoint number".to_owned())
87 } else {
88 parts[1]
89 .parse::<usize>()
90 .map(Command::ClearBreakpoint)
91 .map_err(|_| "Invalid breakpoint number".to_owned())
92 }
93 }
94 "help" | "h" | "?" => Ok(Command::Help),
95 "quit" | "q" | "exit" => Ok(Command::Quit),
96 _ => Err(format!(
97 "Unknown command: '{}'. Type 'help' for commands.",
98 parts[0]
99 )),
100 }
101}
102
103fn parse_breakpoint(parts: &[&str]) -> Result<Command, String> {
104 match parts[0] {
105 "boundary" => Ok(Command::Break(Breakpoint::BoundaryElimination)),
106 "phase" => {
107 if parts.len() < 2 {
108 Err("break phase requires phase name".to_owned())
109 } else {
110 Ok(Command::Break(Breakpoint::Phase(parts[1..].join(" "))))
111 }
112 }
113 "function" | "fn" => {
114 if parts.len() < 2 {
115 Err("break function requires function name".to_owned())
116 } else {
117 Ok(Command::Break(Breakpoint::Function(parts[1].to_owned())))
118 }
119 }
120 _ => Err(format!("Unknown breakpoint type: '{}'", parts[0])),
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_parse_step() {
130 assert_eq!(parse_command("step").unwrap(), Command::Step);
131 assert_eq!(parse_command("s").unwrap(), Command::Step);
132 assert_eq!(parse_command("").unwrap(), Command::Step);
133 }
134
135 #[test]
136 fn test_parse_quit() {
137 assert_eq!(parse_command("quit").unwrap(), Command::Quit);
138 assert_eq!(parse_command("q").unwrap(), Command::Quit);
139 }
140
141 #[test]
142 fn test_parse_inspect() {
143 assert_eq!(
144 parse_command("inspect python_hir").unwrap(),
145 Command::Inspect("python_hir".to_owned())
146 );
147 }
148
149 #[test]
150 fn test_parse_breakpoint() {
151 assert_eq!(
152 parse_command("break boundary").unwrap(),
153 Command::Break(Breakpoint::BoundaryElimination)
154 );
155 }
156}