jetcrab\api/
debug.rs

1use crate::api::error::ApiError;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::time::{Duration, Instant};
5
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct Breakpoint {
8    pub id: String,
9    pub line: usize,
10    pub column: usize,
11    pub condition: Option<String>,
12    pub enabled: bool,
13    pub hit_count: usize,
14}
15
16impl Breakpoint {
17    pub fn new(id: String, line: usize, column: usize) -> Self {
18        Self {
19            id,
20            line,
21            column,
22            condition: None,
23            enabled: true,
24            hit_count: 0,
25        }
26    }
27
28    pub fn with_condition(mut self, condition: String) -> Self {
29        self.condition = Some(condition);
30        self
31    }
32
33    pub fn hit(&mut self) {
34        self.hit_count += 1;
35    }
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39pub struct CallFrame {
40    pub function_name: String,
41    pub line: usize,
42    pub column: usize,
43    pub variables: HashMap<String, crate::vm::value::Value>,
44    pub this_value: Option<crate::vm::value::Value>,
45    pub arguments: Vec<crate::vm::value::Value>,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct DebugInfo {
50    pub current_line: usize,
51    pub current_column: usize,
52    pub call_stack: Vec<CallFrame>,
53    pub breakpoints: Vec<Breakpoint>,
54    pub variables: HashMap<String, crate::vm::value::Value>,
55    pub is_paused: bool,
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub struct ProfilingMetrics {
60    pub execution_time: Duration,
61    pub memory_usage: usize,
62    pub instruction_count: usize,
63    pub function_calls: usize,
64    pub gc_cycles: usize,
65}
66
67impl ProfilingMetrics {
68    pub fn generate_report(&self) -> String {
69        format!(
70            "Profiling Report:\n\
71             Execution Time: {:?}\n\
72             Memory Usage: {} bytes\n\
73             Instructions Executed: {}\n\
74             Function Calls: {}\n\
75             GC Cycles: {}\n\
76             Functions Profiled: {}",
77            self.execution_time,
78            self.memory_usage,
79            self.instruction_count,
80            self.function_calls,
81            self.gc_cycles,
82            0 // Placeholder for function timings count
83        )
84    }
85}
86
87pub struct Debugger {
88    breakpoints: HashMap<String, Breakpoint>,
89    is_enabled: bool,
90    step_mode: bool,
91    current_info: Option<DebugInfo>,
92}
93
94impl Debugger {
95    pub fn new() -> Self {
96        Self {
97            breakpoints: HashMap::new(),
98            is_enabled: false,
99            step_mode: false,
100            current_info: None,
101        }
102    }
103
104    pub fn enable(&mut self) {
105        self.is_enabled = true;
106    }
107
108    pub fn disable(&mut self) {
109        self.is_enabled = false;
110    }
111
112    pub fn add_breakpoint(&mut self, breakpoint: Breakpoint) {
113        self.breakpoints.insert(breakpoint.id.clone(), breakpoint);
114    }
115
116    pub fn remove_breakpoint(&mut self, id: &str) -> Option<Breakpoint> {
117        self.breakpoints.remove(id)
118    }
119
120    pub fn enable_breakpoint(&mut self, id: &str) -> Result<(), ApiError> {
121        if let Some(breakpoint) = self.breakpoints.get_mut(id) {
122            breakpoint.enabled = true;
123            Ok(())
124        } else {
125            Err(ApiError::InvalidInput {
126                message: "Breakpoint not found".to_string(),
127                input: id.to_string(),
128                position: None,
129            })
130        }
131    }
132
133    pub fn disable_breakpoint(&mut self, id: &str) -> Result<(), ApiError> {
134        if let Some(breakpoint) = self.breakpoints.get_mut(id) {
135            breakpoint.enabled = false;
136            Ok(())
137        } else {
138            Err(ApiError::InvalidInput {
139                message: "Breakpoint not found".to_string(),
140                input: id.to_string(),
141                position: None,
142            })
143        }
144    }
145
146    pub fn should_pause(&self, line: usize, column: usize) -> bool {
147        if !self.is_enabled {
148            return false;
149        }
150
151        for breakpoint in self.breakpoints.values() {
152            if breakpoint.enabled && breakpoint.line == line && breakpoint.column == column {
153                return true;
154            }
155        }
156
157        self.step_mode
158    }
159
160    pub fn update_debug_info(&mut self, info: DebugInfo) {
161        self.current_info = Some(info);
162    }
163
164    pub fn get_debug_info(&self) -> Option<&DebugInfo> {
165        self.current_info.as_ref()
166    }
167
168    pub fn step_into(&mut self) {
169        self.step_mode = true;
170    }
171
172    pub fn step_over(&mut self) {
173        self.step_mode = false;
174    }
175
176    pub fn continue_execution(&mut self) {
177        self.step_mode = false;
178    }
179}
180
181impl Default for Debugger {
182    fn default() -> Self {
183        Self::new()
184    }
185}
186
187pub struct Profiler {
188    start_time: Option<Instant>,
189    metrics: ProfilingMetrics,
190    function_timings: HashMap<String, Duration>,
191    memory_snapshots: Vec<(Instant, usize)>,
192}
193
194impl Profiler {
195    pub fn new() -> Self {
196        Self {
197            start_time: None,
198            metrics: ProfilingMetrics {
199                execution_time: Duration::ZERO,
200                memory_usage: 0,
201                instruction_count: 0,
202                function_calls: 0,
203                gc_cycles: 0,
204            },
205            function_timings: HashMap::new(),
206            memory_snapshots: Vec::new(),
207        }
208    }
209
210    pub fn start_profiling(&mut self) {
211        self.start_time = Some(Instant::now());
212    }
213
214    pub fn stop_profiling(&mut self) -> ProfilingMetrics {
215        if let Some(start_time) = self.start_time {
216            self.metrics.execution_time = start_time.elapsed();
217        }
218        self.metrics.clone()
219    }
220
221    pub fn record_function_call(&mut self, function_name: String, duration: Duration) {
222        *self
223            .function_timings
224            .entry(function_name)
225            .or_insert(Duration::ZERO) += duration;
226        self.metrics.function_calls += 1;
227    }
228
229    pub fn record_instruction(&mut self) {
230        self.metrics.instruction_count += 1;
231    }
232
233    pub fn record_memory_usage(&mut self, usage: usize) {
234        self.metrics.memory_usage = usage;
235        self.memory_snapshots.push((Instant::now(), usage));
236    }
237
238    pub fn record_gc_cycle(&mut self) {
239        self.metrics.gc_cycles += 1;
240    }
241
242    pub fn get_function_timings(&self) -> &HashMap<String, Duration> {
243        &self.function_timings
244    }
245
246    pub fn get_memory_snapshots(&self) -> &Vec<(Instant, usize)> {
247        &self.memory_snapshots
248    }
249}
250
251impl Default for Profiler {
252    fn default() -> Self {
253        Self::new()
254    }
255}
256
257pub struct Inspector {
258    debugger: Debugger,
259    profiler: Profiler,
260    event_listeners: HashMap<String, Vec<Box<dyn Fn(String) + Send + Sync>>>,
261}
262
263impl Inspector {
264    pub fn new() -> Self {
265        Self {
266            debugger: Debugger::new(),
267            profiler: Profiler::new(),
268            event_listeners: HashMap::new(),
269        }
270    }
271
272    pub fn get_debugger(&mut self) -> &mut Debugger {
273        &mut self.debugger
274    }
275
276    pub fn get_profiler(&mut self) -> &mut Profiler {
277        &mut self.profiler
278    }
279
280    pub fn add_event_listener(
281        &mut self,
282        event: String,
283        listener: Box<dyn Fn(String) + Send + Sync>,
284    ) {
285        self.event_listeners
286            .entry(event)
287            .or_insert_with(Vec::new)
288            .push(listener);
289    }
290
291    pub fn emit_event(&self, event: &str, data: String) {
292        if let Some(listeners) = self.event_listeners.get(event) {
293            for listener in listeners {
294                listener(data.clone());
295            }
296        }
297    }
298
299    pub fn start_inspection(&mut self) {
300        self.debugger.enable();
301        self.profiler.start_profiling();
302        self.emit_event(
303            "inspection_started",
304            "Debugging and profiling started".to_string(),
305        );
306    }
307
308    pub fn stop_inspection(&mut self) -> String {
309        self.debugger.disable();
310        let report = self.profiler.stop_profiling();
311        self.emit_event(
312            "inspection_stopped",
313            "Debugging and profiling stopped".to_string(),
314        );
315        report.generate_report()
316    }
317}
318
319impl Default for Inspector {
320    fn default() -> Self {
321        Self::new()
322    }
323}
324
325#[cfg(test)]
326mod tests {
327    use super::*;
328    use std::time::Duration;
329
330    #[test]
331    fn test_breakpoint() {
332        let mut breakpoint = Breakpoint::new("bp1".to_string(), 10, 5);
333        breakpoint.hit();
334        assert_eq!(breakpoint.hit_count, 1);
335    }
336
337    #[test]
338    fn test_debugger() {
339        let mut debugger = Debugger::new();
340        let breakpoint = Breakpoint::new("bp1".to_string(), 10, 5);
341        debugger.add_breakpoint(breakpoint);
342
343        // Enable the debugger first
344        debugger.enable();
345
346        assert!(debugger.should_pause(10, 5));
347        assert!(!debugger.should_pause(11, 5));
348    }
349
350    #[test]
351    fn test_profiler() {
352        let mut profiler = Profiler::new();
353        profiler.start_profiling();
354        std::thread::sleep(Duration::from_millis(10));
355        let metrics = profiler.stop_profiling();
356
357        assert!(metrics.execution_time > Duration::ZERO);
358    }
359
360    #[test]
361    fn test_inspector() {
362        let mut inspector = Inspector::new();
363
364        inspector.add_event_listener(
365            "test".to_string(),
366            Box::new(|_| {
367                // In a real test, we'd use a different approach to verify the event was received
368                // For now, just verify the listener was added
369            }),
370        );
371
372        // Test that the listener was added (this is a simplified test)
373        assert!(true); // Placeholder assertion
374    }
375}