1use crate::debugger::breakpoint::{Breakpoint, BreakpointType};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, PartialEq)]
9pub enum ExecutionMode {
10 Normal,
12 StepInto,
14 StepOver,
16 StepOut,
18 Continue,
20}
21
22pub struct DebuggerState {
24 breakpoints: HashMap<usize, Breakpoint>,
26 next_breakpoint_id: usize,
28 execution_mode: ExecutionMode,
30 current_location: Option<(String, usize)>,
32 step_over_depth: Option<usize>,
34 is_active: bool,
36}
37
38impl DebuggerState {
39 pub fn new() -> Self {
41 DebuggerState {
42 breakpoints: HashMap::new(),
43 next_breakpoint_id: 1,
44 execution_mode: ExecutionMode::Normal,
45 current_location: None,
46 step_over_depth: None,
47 is_active: false,
48 }
49 }
50
51 pub fn activate(&mut self) {
53 self.is_active = true;
54 }
55
56 pub fn deactivate(&mut self) {
58 self.is_active = false;
59 }
60
61 pub fn is_active(&self) -> bool {
63 self.is_active
64 }
65
66 pub fn set_breakpoint(&mut self, bp_type: BreakpointType) -> usize {
68 let id = self.next_breakpoint_id;
69 self.next_breakpoint_id += 1;
70 self.breakpoints.insert(id, Breakpoint::new(id, bp_type));
71 id
72 }
73
74 pub fn remove_breakpoint(&mut self, id: usize) -> bool {
76 self.breakpoints.remove(&id).is_some()
77 }
78
79 pub fn remove_all_breakpoints(&mut self) {
81 self.breakpoints.clear();
82 }
83
84 pub fn toggle_breakpoint(&mut self, id: usize, enabled: bool) -> bool {
86 if let Some(bp) = self.breakpoints.get_mut(&id) {
87 bp.enabled = enabled;
88 true
89 } else {
90 false
91 }
92 }
93
94 pub fn get_breakpoint(&self, id: usize) -> Option<&Breakpoint> {
96 self.breakpoints.get(&id)
97 }
98
99 pub fn list_breakpoints(&self) -> Vec<&Breakpoint> {
101 let mut bps: Vec<_> = self.breakpoints.values().collect();
102 bps.sort_by_key(|bp| bp.id);
103 bps
104 }
105
106 pub fn set_execution_mode(&mut self, mode: ExecutionMode) {
108 self.execution_mode = mode;
109 }
110
111 pub fn execution_mode(&self) -> &ExecutionMode {
113 &self.execution_mode
114 }
115
116 pub fn set_step_over_depth(&mut self, depth: usize) {
118 self.step_over_depth = Some(depth);
119 }
120
121 pub fn clear_step_over_depth(&mut self) {
123 self.step_over_depth = None;
124 }
125
126 pub fn update_location(&mut self, file: String, line: usize) {
128 self.current_location = Some((file, line));
129 }
130
131 pub fn current_location(&self) -> Option<&(String, usize)> {
133 self.current_location.as_ref()
134 }
135
136 pub fn should_pause_at_function(&mut self, func_name: &str) -> bool {
138 if !self.is_active {
139 return false;
140 }
141
142 for bp in self.breakpoints.values_mut() {
143 if bp.is_function_breakpoint(func_name) && bp.enabled {
144 bp.hit_count += 1;
145 if bp.hit_count > bp.ignore_count {
146 return true;
147 }
148 }
149 }
150 false
151 }
152
153 pub fn should_pause(&mut self, file: &str, line: usize, call_stack_depth: usize) -> bool {
155 if !self.is_active {
156 return false;
157 }
158
159 self.current_location = Some((file.to_string(), line));
161
162 match self.execution_mode {
163 ExecutionMode::Normal => {
164 for bp in self.breakpoints.values_mut() {
166 if bp.should_trigger(file, line) {
167 return true;
168 }
169 }
170 false
171 }
172 ExecutionMode::StepInto => {
173 self.execution_mode = ExecutionMode::Normal;
175 true
176 }
177 ExecutionMode::StepOver => {
178 if let Some(target_depth) = self.step_over_depth
180 && call_stack_depth <= target_depth
181 {
182 self.step_over_depth = None;
183 self.execution_mode = ExecutionMode::Normal;
184 return true;
185 }
186 false
187 }
188 ExecutionMode::StepOut => {
189 if let Some(target_depth) = self.step_over_depth
191 && call_stack_depth < target_depth
192 {
193 self.step_over_depth = None;
194 self.execution_mode = ExecutionMode::Normal;
195 return true;
196 }
197 false
198 }
199 ExecutionMode::Continue => {
200 for bp in self.breakpoints.values_mut() {
202 if bp.should_trigger(file, line) {
203 self.execution_mode = ExecutionMode::Normal;
204 return true;
205 }
206 }
207 false
208 }
209 }
210 }
211}
212
213impl Default for DebuggerState {
214 fn default() -> Self {
215 Self::new()
216 }
217}
218
219#[cfg(test)]
220mod tests {
221 use super::*;
222
223 #[test]
224 fn test_set_breakpoint() {
225 let mut state = DebuggerState::new();
226 let id = state.set_breakpoint(BreakpointType::Line {
227 file: "test.aether".to_string(),
228 line: 10,
229 });
230
231 assert_eq!(id, 1);
232 assert!(state.get_breakpoint(id).is_some());
233 }
234
235 #[test]
236 fn test_remove_breakpoint() {
237 let mut state = DebuggerState::new();
238 let id = state.set_breakpoint(BreakpointType::Line {
239 file: "test.aether".to_string(),
240 line: 10,
241 });
242
243 assert!(state.remove_breakpoint(id));
244 assert!(!state.remove_breakpoint(id));
245 assert!(state.get_breakpoint(id).is_none());
246 }
247
248 #[test]
249 fn test_toggle_breakpoint() {
250 let mut state = DebuggerState::new();
251 let id = state.set_breakpoint(BreakpointType::Line {
252 file: "test.aether".to_string(),
253 line: 10,
254 });
255
256 assert!(state.toggle_breakpoint(id, false));
257 assert!(!state.get_breakpoint(id).unwrap().enabled);
258 assert!(state.toggle_breakpoint(id, true));
259 assert!(state.get_breakpoint(id).unwrap().enabled);
260 }
261
262 #[test]
263 fn test_should_pause_normal_mode() {
264 let mut state = DebuggerState::new();
265 state.activate();
266
267 let _: usize = state.set_breakpoint(BreakpointType::Line {
268 file: "test.aether".to_string(),
269 line: 10,
270 });
271
272 assert!(state.should_pause("test.aether", 10, 0));
273 assert!(!state.should_pause("test.aether", 11, 0));
274 }
275
276 #[test]
277 fn test_step_into_mode() {
278 let mut state = DebuggerState::new();
279 state.activate();
280 state.set_execution_mode(ExecutionMode::StepInto);
281
282 assert!(state.should_pause("test.aether", 10, 0));
284 assert_eq!(state.execution_mode(), &ExecutionMode::Normal);
286 }
287
288 #[test]
289 fn test_step_over_mode() {
290 let mut state = DebuggerState::new();
291 state.activate();
292 state.set_execution_mode(ExecutionMode::StepOver);
293 state.set_step_over_depth(2);
294
295 assert!(!state.should_pause("test.aether", 10, 3));
297 assert!(state.should_pause("test.aether", 11, 2));
299 }
300
301 #[test]
302 fn test_step_out_mode() {
303 let mut state = DebuggerState::new();
304 state.activate();
305 state.set_execution_mode(ExecutionMode::StepOut);
306 state.set_step_over_depth(2);
307
308 assert!(!state.should_pause("test.aether", 10, 2));
310 assert!(state.should_pause("test.aether", 11, 1));
312 }
313
314 #[test]
315 fn test_function_breakpoint() {
316 let mut state = DebuggerState::new();
317 state.activate();
318 state.set_breakpoint(BreakpointType::Function {
319 name: "myFunc".to_string(),
320 });
321
322 assert!(state.should_pause_at_function("myFunc"));
323 assert!(!state.should_pause_at_function("otherFunc"));
324 }
325}