Skip to main content

spydecy_debugger/
commands.rs

1//! Debugger Command Structures
2//!
3//! Defines commands available in the interactive debugger REPL.
4
5use std::fmt;
6
7/// Debugger commands
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum Command {
10    /// Step to next transpilation phase
11    Step,
12    /// Continue until breakpoint or completion
13    Continue,
14    /// Visualize current state
15    Visualize,
16    /// Inspect a specific target
17    Inspect(String),
18    /// Add a breakpoint
19    Break(Breakpoint),
20    /// List all breakpoints
21    ListBreakpoints,
22    /// Clear a breakpoint
23    ClearBreakpoint(usize),
24    /// Show help
25    Help,
26    /// Quit debugger
27    Quit,
28}
29
30/// Breakpoint types
31#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum Breakpoint {
33    /// Break when optimizer eliminates a boundary
34    BoundaryElimination,
35    /// Break when entering a specific phase
36    Phase(String),
37    /// Break when processing specific function
38    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
51/// Parse command from user input
52///
53/// # Errors
54///
55/// Returns error if command syntax is invalid
56pub fn parse_command(input: &str) -> Result<Command, String> {
57    let input = input.trim();
58
59    if input.is_empty() {
60        return Ok(Command::Step); // Default to step
61    }
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}