Skip to main content

depyler_tooling/
debug.rs

1//! Debugging support for Depyler
2//!
3//! This module provides debugging features including:
4//! - Source map generation
5//! - Debug symbol preservation
6//! - Debugger integration helpers
7//! - Runtime debugging utilities
8
9use depyler_hir::hir::{HirFunction, Type};
10use serde::{Deserialize, Serialize};
11use std::collections::HashMap;
12use std::path::PathBuf;
13
14/// Source mapping information
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct SourceMap {
17    /// Original Python source file
18    pub source_file: PathBuf,
19    /// Generated Rust file
20    pub target_file: PathBuf,
21    /// Mapping entries
22    pub mappings: Vec<SourceMapping>,
23    /// Function mappings
24    pub function_map: HashMap<String, FunctionMapping>,
25}
26
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct SourceMapping {
29    /// Python source location
30    pub python_line: usize,
31    pub python_column: usize,
32    /// Rust target location
33    pub rust_line: usize,
34    pub rust_column: usize,
35    /// Optional symbol name
36    pub symbol: Option<String>,
37}
38
39#[derive(Debug, Clone, Serialize, Deserialize)]
40pub struct FunctionMapping {
41    pub python_name: String,
42    pub rust_name: String,
43    pub python_start_line: usize,
44    pub python_end_line: usize,
45    pub rust_start_line: usize,
46    pub rust_end_line: usize,
47}
48
49/// Debug information generator
50pub struct DebugInfoGenerator {
51    source_map: SourceMap,
52    current_rust_line: usize,
53    debug_level: DebugLevel,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
57pub enum DebugLevel {
58    /// No debug information
59    None,
60    /// Basic line mappings
61    Basic,
62    /// Full debug information with symbols
63    Full,
64}
65
66impl DebugInfoGenerator {
67    pub fn new(source_file: PathBuf, target_file: PathBuf, debug_level: DebugLevel) -> Self {
68        Self {
69            source_map: SourceMap {
70                source_file,
71                target_file,
72                mappings: Vec::new(),
73                function_map: HashMap::new(),
74            },
75            current_rust_line: 1,
76            debug_level,
77        }
78    }
79
80    /// Add a source mapping
81    pub fn add_mapping(
82        &mut self,
83        python_line: usize,
84        python_column: usize,
85        symbol: Option<String>,
86    ) {
87        if self.debug_level == DebugLevel::None {
88            return;
89        }
90
91        self.source_map.mappings.push(SourceMapping {
92            python_line,
93            python_column,
94            rust_line: self.current_rust_line,
95            rust_column: 0, // Simplified for now
96            symbol,
97        });
98    }
99
100    /// Add a function mapping
101    pub fn add_function_mapping(&mut self, func: &HirFunction, rust_start: usize) {
102        if self.debug_level == DebugLevel::None {
103            return;
104        }
105
106        let rust_end = self.current_rust_line;
107        self.source_map.function_map.insert(
108            func.name.clone(),
109            FunctionMapping {
110                python_name: func.name.clone(),
111                rust_name: func.name.clone(), // Could be mangled
112                python_start_line: 0,         // Would need source location
113                python_end_line: 0,
114                rust_start_line: rust_start,
115                rust_end_line: rust_end,
116            },
117        );
118    }
119
120    /// Increment line counter (for tracking generated code)
121    pub fn new_line(&mut self) {
122        self.current_rust_line += 1;
123    }
124
125    /// Get the source map
126    pub fn source_map(&self) -> &SourceMap {
127        &self.source_map
128    }
129
130    /// Generate debug annotations for a function
131    pub fn generate_function_debug(&self, func: &HirFunction) -> String {
132        match self.debug_level {
133            DebugLevel::None => String::new(),
134            DebugLevel::Basic => format!("// Function: {}\n", func.name),
135            DebugLevel::Full => {
136                format!(
137                    "// Function: {} (Python source)\n// Parameters: {:?}\n// Returns: {:?}\n",
138                    func.name,
139                    func.params.iter().map(|p| &p.name).collect::<Vec<_>>(),
140                    func.ret_type
141                )
142            }
143        }
144    }
145
146    /// Generate debug print for a variable
147    pub fn generate_debug_print(&self, var_name: &str, var_type: &Type) -> String {
148        match self.debug_level {
149            DebugLevel::None => String::new(),
150            DebugLevel::Basic | DebugLevel::Full => match var_type {
151                Type::Int | Type::Float | Type::Bool => {
152                    format!("eprintln!(\"DEBUG: {} = {{}}\", {});", var_name, var_name)
153                }
154                Type::String => {
155                    format!("eprintln!(\"DEBUG: {} = {{}}\", {});", var_name, var_name)
156                }
157                _ => {
158                    format!("eprintln!(\"DEBUG: {} = {{:?}}\", {});", var_name, var_name)
159                }
160            },
161        }
162    }
163}
164
165/// Runtime debugging utilities
166pub struct DebugRuntime;
167
168impl DebugRuntime {
169    /// Generate a breakpoint macro
170    pub fn breakpoint() -> &'static str {
171        "depyler_breakpoint!()"
172    }
173
174    /// Generate an assertion with debug info
175    pub fn debug_assert(condition: &str, message: &str) -> String {
176        format!("debug_assert!({}, \"{}\");", condition, message)
177    }
178
179    /// Generate a trace point
180    pub fn trace_point(location: &str) -> String {
181        format!("depyler_trace!(\"{}\");", location)
182    }
183}
184
185/// Debugger integration helpers
186pub struct DebuggerIntegration {
187    debugger_type: DebuggerType,
188}
189
190#[derive(Debug, Clone, Copy)]
191pub enum DebuggerType {
192    Gdb,
193    Lldb,
194    RustGdb,
195}
196
197impl DebuggerIntegration {
198    pub fn new(debugger_type: DebuggerType) -> Self {
199        Self { debugger_type }
200    }
201
202    /// Generate debugger initialization script
203    pub fn generate_init_script(&self, source_map: &SourceMap) -> String {
204        match self.debugger_type {
205            DebuggerType::Gdb | DebuggerType::RustGdb => self.generate_gdb_script(source_map),
206            DebuggerType::Lldb => self.generate_lldb_script(source_map),
207        }
208    }
209
210    fn generate_gdb_script(&self, source_map: &SourceMap) -> String {
211        let mut script = String::new();
212        script.push_str("# GDB initialization script for Depyler debugging\n");
213        script.push_str("# Source: ");
214        script.push_str(&source_map.source_file.display().to_string());
215        script.push_str("\n\n");
216
217        // Add source path
218        script.push_str("directory .\n");
219
220        // Add function breakpoints
221        for mapping in source_map.function_map.values() {
222            script.push_str(&format!("break {}\n", mapping.rust_name));
223        }
224
225        // Pretty printers for Rust types
226        if matches!(self.debugger_type, DebuggerType::RustGdb) {
227            script.push_str("\n# Load Rust pretty printers\n");
228            script.push_str("python\nimport gdb\n");
229            script.push_str("gdb.execute('set print pretty on')\n");
230            script.push_str("end\n");
231        }
232
233        script
234    }
235
236    fn generate_lldb_script(&self, source_map: &SourceMap) -> String {
237        let mut script = String::new();
238        script.push_str("# LLDB initialization script for Depyler debugging\n");
239        script.push_str("# Source: ");
240        script.push_str(&source_map.source_file.display().to_string());
241        script.push_str("\n\n");
242
243        // Add source mapping
244        script.push_str("settings set target.source-map . .\n");
245
246        // Add function breakpoints
247        for mapping in source_map.function_map.values() {
248            script.push_str(&format!("breakpoint set --name {}\n", mapping.rust_name));
249        }
250
251        script
252    }
253}
254
255/// Debug configuration
256#[derive(Debug, Clone, Serialize, Deserialize)]
257pub struct DebugConfig {
258    pub debug_level: DebugLevel,
259    pub generate_source_map: bool,
260    pub preserve_symbols: bool,
261    pub debug_prints: bool,
262    pub breakpoints: bool,
263}
264
265impl Default for DebugConfig {
266    fn default() -> Self {
267        Self {
268            debug_level: DebugLevel::Basic,
269            generate_source_map: true,
270            preserve_symbols: true,
271            debug_prints: false,
272            breakpoints: false,
273        }
274    }
275}
276
277/// Helper macros for generated code
278pub fn generate_debug_macros() -> String {
279    r#"
280// Depyler debugging macros
281#[macro_export]
282macro_rules! depyler_breakpoint {
283    () => {
284        #[cfg(debug_assertions)]
285        {
286            eprintln!("BREAKPOINT at {}:{}", file!(), line!());
287            // Uncomment to actually break in debugger
288            // std::intrinsics::breakpoint();
289        }
290    };
291}
292
293#[macro_export]
294macro_rules! depyler_trace {
295    ($msg:expr) => {
296        #[cfg(debug_assertions)]
297        eprintln!("[TRACE] {} at {}:{}", $msg, file!(), line!());
298    };
299}
300"#
301    .to_string()
302}
303
304#[cfg(test)]
305mod tests {
306    use super::*;
307    use depyler_hir::hir::{FunctionProperties, HirParam};
308    use smallvec::smallvec;
309
310    // === SourceMap tests ===
311
312    #[test]
313    fn test_source_map_new() {
314        let sm = SourceMap {
315            source_file: PathBuf::from("input.py"),
316            target_file: PathBuf::from("output.rs"),
317            mappings: vec![],
318            function_map: HashMap::new(),
319        };
320        assert_eq!(sm.source_file, PathBuf::from("input.py"));
321        assert_eq!(sm.target_file, PathBuf::from("output.rs"));
322        assert!(sm.mappings.is_empty());
323    }
324
325    #[test]
326    fn test_source_map_clone() {
327        let sm = SourceMap {
328            source_file: PathBuf::from("a.py"),
329            target_file: PathBuf::from("a.rs"),
330            mappings: vec![SourceMapping {
331                python_line: 1,
332                python_column: 0,
333                rust_line: 1,
334                rust_column: 0,
335                symbol: None,
336            }],
337            function_map: HashMap::new(),
338        };
339        let cloned = sm.clone();
340        assert_eq!(cloned.mappings.len(), 1);
341    }
342
343    #[test]
344    fn test_source_map_serialize() {
345        let sm = SourceMap {
346            source_file: PathBuf::from("test.py"),
347            target_file: PathBuf::from("test.rs"),
348            mappings: vec![],
349            function_map: HashMap::new(),
350        };
351        let json = serde_json::to_string(&sm).unwrap();
352        assert!(json.contains("test.py"));
353        let deserialized: SourceMap = serde_json::from_str(&json).unwrap();
354        assert_eq!(deserialized.source_file, sm.source_file);
355    }
356
357    // === SourceMapping tests ===
358
359    #[test]
360    fn test_source_mapping_new() {
361        let mapping = SourceMapping {
362            python_line: 42,
363            python_column: 8,
364            rust_line: 100,
365            rust_column: 4,
366            symbol: Some("my_func".to_string()),
367        };
368        assert_eq!(mapping.python_line, 42);
369        assert_eq!(mapping.rust_line, 100);
370        assert_eq!(mapping.symbol, Some("my_func".to_string()));
371    }
372
373    #[test]
374    fn test_source_mapping_clone() {
375        let mapping = SourceMapping {
376            python_line: 1,
377            python_column: 0,
378            rust_line: 5,
379            rust_column: 0,
380            symbol: None,
381        };
382        let cloned = mapping.clone();
383        assert_eq!(cloned.python_line, mapping.python_line);
384    }
385
386    #[test]
387    fn test_source_mapping_serialize() {
388        let mapping = SourceMapping {
389            python_line: 10,
390            python_column: 2,
391            rust_line: 20,
392            rust_column: 4,
393            symbol: Some("var".to_string()),
394        };
395        let json = serde_json::to_string(&mapping).unwrap();
396        assert!(json.contains("10"));
397        assert!(json.contains("var"));
398    }
399
400    // === FunctionMapping tests ===
401
402    #[test]
403    fn test_function_mapping_new() {
404        let fm = FunctionMapping {
405            python_name: "py_func".to_string(),
406            rust_name: "rust_func".to_string(),
407            python_start_line: 5,
408            python_end_line: 15,
409            rust_start_line: 10,
410            rust_end_line: 30,
411        };
412        assert_eq!(fm.python_name, "py_func");
413        assert_eq!(fm.rust_name, "rust_func");
414    }
415
416    #[test]
417    fn test_function_mapping_clone() {
418        let fm = FunctionMapping {
419            python_name: "f".to_string(),
420            rust_name: "f".to_string(),
421            python_start_line: 1,
422            python_end_line: 2,
423            rust_start_line: 3,
424            rust_end_line: 4,
425        };
426        let cloned = fm.clone();
427        assert_eq!(cloned.python_name, fm.python_name);
428    }
429
430    // === DebugLevel tests ===
431
432    #[test]
433    fn test_debug_level_none() {
434        assert_eq!(DebugLevel::None, DebugLevel::None);
435        assert_ne!(DebugLevel::None, DebugLevel::Basic);
436    }
437
438    #[test]
439    fn test_debug_level_basic() {
440        assert_eq!(DebugLevel::Basic, DebugLevel::Basic);
441    }
442
443    #[test]
444    fn test_debug_level_full() {
445        let level = DebugLevel::Full;
446        let cloned = level;
447        assert_eq!(cloned, DebugLevel::Full);
448    }
449
450    #[test]
451    fn test_debug_level_serialize() {
452        let level = DebugLevel::Full;
453        let json = serde_json::to_string(&level).unwrap();
454        let deserialized: DebugLevel = serde_json::from_str(&json).unwrap();
455        assert_eq!(deserialized, level);
456    }
457
458    // === DebugInfoGenerator tests ===
459
460    #[test]
461    fn test_debug_info_generator_new() {
462        let gen = DebugInfoGenerator::new(
463            PathBuf::from("src.py"),
464            PathBuf::from("src.rs"),
465            DebugLevel::Basic,
466        );
467        assert_eq!(gen.current_rust_line, 1);
468        assert_eq!(gen.debug_level, DebugLevel::Basic);
469    }
470
471    #[test]
472    fn test_debug_info_generator_add_mapping_none_level() {
473        let mut gen = DebugInfoGenerator::new(
474            PathBuf::from("a.py"),
475            PathBuf::from("a.rs"),
476            DebugLevel::None,
477        );
478        gen.add_mapping(10, 0, Some("test".to_string()));
479        // Should not add mappings when debug level is None
480        assert!(gen.source_map().mappings.is_empty());
481    }
482
483    #[test]
484    fn test_debug_info_generator_new_line() {
485        let mut gen = DebugInfoGenerator::new(
486            PathBuf::from("a.py"),
487            PathBuf::from("a.rs"),
488            DebugLevel::Basic,
489        );
490        assert_eq!(gen.current_rust_line, 1);
491        gen.new_line();
492        assert_eq!(gen.current_rust_line, 2);
493        gen.new_line();
494        gen.new_line();
495        assert_eq!(gen.current_rust_line, 4);
496    }
497
498    #[test]
499    fn test_debug_info_generator_source_map_getter() {
500        let gen = DebugInfoGenerator::new(
501            PathBuf::from("test.py"),
502            PathBuf::from("test.rs"),
503            DebugLevel::Full,
504        );
505        let sm = gen.source_map();
506        assert_eq!(sm.source_file, PathBuf::from("test.py"));
507    }
508
509    #[test]
510    fn test_generate_function_debug_none_level() {
511        let gen = DebugInfoGenerator::new(
512            PathBuf::from("a.py"),
513            PathBuf::from("a.rs"),
514            DebugLevel::None,
515        );
516        let func = HirFunction {
517            name: "test".to_string(),
518            params: smallvec![],
519            ret_type: Type::None,
520            body: vec![],
521            properties: FunctionProperties::default(),
522            annotations: depyler_annotations::TranspilationAnnotations::default(),
523            docstring: None,
524        };
525        let debug = gen.generate_function_debug(&func);
526        assert!(debug.is_empty());
527    }
528
529    #[test]
530    fn test_generate_function_debug_basic_level() {
531        let gen = DebugInfoGenerator::new(
532            PathBuf::from("a.py"),
533            PathBuf::from("a.rs"),
534            DebugLevel::Basic,
535        );
536        let func = HirFunction {
537            name: "my_func".to_string(),
538            params: smallvec![],
539            ret_type: Type::Int,
540            body: vec![],
541            properties: FunctionProperties::default(),
542            annotations: depyler_annotations::TranspilationAnnotations::default(),
543            docstring: None,
544        };
545        let debug = gen.generate_function_debug(&func);
546        assert!(debug.contains("// Function: my_func"));
547        assert!(!debug.contains("Parameters")); // Basic doesn't include params
548    }
549
550    #[test]
551    fn test_generate_function_debug_full_level() {
552        let gen = DebugInfoGenerator::new(
553            PathBuf::from("a.py"),
554            PathBuf::from("a.rs"),
555            DebugLevel::Full,
556        );
557        let func = HirFunction {
558            name: "calc".to_string(),
559            params: smallvec![HirParam::new("x".to_string(), Type::Int)],
560            ret_type: Type::Int,
561            body: vec![],
562            properties: FunctionProperties::default(),
563            annotations: depyler_annotations::TranspilationAnnotations::default(),
564            docstring: None,
565        };
566        let debug = gen.generate_function_debug(&func);
567        assert!(debug.contains("// Function: calc"));
568        assert!(debug.contains("Parameters"));
569        assert!(debug.contains("Returns"));
570    }
571
572    #[test]
573    fn test_generate_debug_print_none_level() {
574        let gen = DebugInfoGenerator::new(
575            PathBuf::from("a.py"),
576            PathBuf::from("a.rs"),
577            DebugLevel::None,
578        );
579        let debug = gen.generate_debug_print("x", &Type::Int);
580        assert!(debug.is_empty());
581    }
582
583    #[test]
584    fn test_generate_debug_print_float() {
585        let gen = DebugInfoGenerator::new(
586            PathBuf::from("a.py"),
587            PathBuf::from("a.rs"),
588            DebugLevel::Basic,
589        );
590        let debug = gen.generate_debug_print("value", &Type::Float);
591        assert!(debug.contains("eprintln!"));
592        assert!(debug.contains("value = {}"));
593    }
594
595    #[test]
596    fn test_generate_debug_print_bool() {
597        let gen = DebugInfoGenerator::new(
598            PathBuf::from("a.py"),
599            PathBuf::from("a.rs"),
600            DebugLevel::Full,
601        );
602        let debug = gen.generate_debug_print("flag", &Type::Bool);
603        assert!(debug.contains("flag = {}"));
604    }
605
606    #[test]
607    fn test_generate_debug_print_string() {
608        let gen = DebugInfoGenerator::new(
609            PathBuf::from("a.py"),
610            PathBuf::from("a.rs"),
611            DebugLevel::Basic,
612        );
613        let debug = gen.generate_debug_print("name", &Type::String);
614        assert!(debug.contains("name = {}"));
615    }
616
617    #[test]
618    fn test_add_function_mapping_none_level() {
619        let mut gen = DebugInfoGenerator::new(
620            PathBuf::from("a.py"),
621            PathBuf::from("a.rs"),
622            DebugLevel::None,
623        );
624        let func = HirFunction {
625            name: "test".to_string(),
626            params: smallvec![],
627            ret_type: Type::None,
628            body: vec![],
629            properties: FunctionProperties::default(),
630            annotations: depyler_annotations::TranspilationAnnotations::default(),
631            docstring: None,
632        };
633        gen.add_function_mapping(&func, 1);
634        assert!(gen.source_map().function_map.is_empty());
635    }
636
637    #[test]
638    fn test_add_function_mapping_full_level() {
639        let mut gen = DebugInfoGenerator::new(
640            PathBuf::from("a.py"),
641            PathBuf::from("a.rs"),
642            DebugLevel::Full,
643        );
644        gen.new_line(); // line 2
645        gen.new_line(); // line 3
646        let func = HirFunction {
647            name: "my_func".to_string(),
648            params: smallvec![],
649            ret_type: Type::Int,
650            body: vec![],
651            properties: FunctionProperties::default(),
652            annotations: depyler_annotations::TranspilationAnnotations::default(),
653            docstring: None,
654        };
655        gen.add_function_mapping(&func, 1);
656        assert!(gen.source_map().function_map.contains_key("my_func"));
657        let fm = gen.source_map().function_map.get("my_func").unwrap();
658        assert_eq!(fm.rust_start_line, 1);
659        assert_eq!(fm.rust_end_line, 3);
660    }
661
662    // === DebugRuntime tests ===
663
664    #[test]
665    fn test_debug_runtime_breakpoint() {
666        let bp = DebugRuntime::breakpoint();
667        assert_eq!(bp, "depyler_breakpoint!()");
668    }
669
670    #[test]
671    fn test_debug_runtime_debug_assert() {
672        let assertion = DebugRuntime::debug_assert("x > 0", "x must be positive");
673        assert!(assertion.contains("debug_assert!"));
674        assert!(assertion.contains("x > 0"));
675        assert!(assertion.contains("x must be positive"));
676    }
677
678    #[test]
679    fn test_debug_runtime_trace_point() {
680        let trace = DebugRuntime::trace_point("entering loop");
681        assert!(trace.contains("depyler_trace!"));
682        assert!(trace.contains("entering loop"));
683    }
684
685    // === DebuggerType tests ===
686
687    #[test]
688    fn test_debugger_type_gdb() {
689        let dt = DebuggerType::Gdb;
690        let debug = format!("{:?}", dt);
691        assert!(debug.contains("Gdb"));
692    }
693
694    #[test]
695    fn test_debugger_type_lldb() {
696        let dt = DebuggerType::Lldb;
697        let cloned = dt;
698        assert!(matches!(cloned, DebuggerType::Lldb));
699    }
700
701    #[test]
702    fn test_debugger_type_rust_gdb() {
703        let dt = DebuggerType::RustGdb;
704        let debug = format!("{:?}", dt);
705        assert!(debug.contains("RustGdb"));
706    }
707
708    // === DebuggerIntegration tests ===
709
710    #[test]
711    fn test_debugger_integration_new() {
712        let di = DebuggerIntegration::new(DebuggerType::Gdb);
713        assert!(matches!(di.debugger_type, DebuggerType::Gdb));
714    }
715
716    #[test]
717    fn test_generate_rust_gdb_script() {
718        let source_map = SourceMap {
719            source_file: PathBuf::from("test.py"),
720            target_file: PathBuf::from("test.rs"),
721            mappings: vec![],
722            function_map: HashMap::new(),
723        };
724        let di = DebuggerIntegration::new(DebuggerType::RustGdb);
725        let script = di.generate_init_script(&source_map);
726        assert!(script.contains("GDB initialization"));
727        assert!(script.contains("Rust pretty printers"));
728        assert!(script.contains("python"));
729    }
730
731    // === DebugConfig tests ===
732
733    #[test]
734    fn test_debug_config_default() {
735        let config = DebugConfig::default();
736        assert_eq!(config.debug_level, DebugLevel::Basic);
737        assert!(config.generate_source_map);
738        assert!(config.preserve_symbols);
739        assert!(!config.debug_prints);
740        assert!(!config.breakpoints);
741    }
742
743    #[test]
744    fn test_debug_config_clone() {
745        let config = DebugConfig {
746            debug_level: DebugLevel::Full,
747            generate_source_map: false,
748            preserve_symbols: false,
749            debug_prints: true,
750            breakpoints: true,
751        };
752        let cloned = config.clone();
753        assert_eq!(cloned.debug_level, DebugLevel::Full);
754        assert!(cloned.debug_prints);
755    }
756
757    #[test]
758    fn test_debug_config_serialize() {
759        let config = DebugConfig::default();
760        let json = serde_json::to_string(&config).unwrap();
761        assert!(json.contains("Basic"));
762        let deserialized: DebugConfig = serde_json::from_str(&json).unwrap();
763        assert_eq!(deserialized.debug_level, config.debug_level);
764    }
765
766    // === generate_debug_macros tests ===
767
768    #[test]
769    fn test_generate_debug_macros() {
770        let macros = generate_debug_macros();
771        assert!(macros.contains("depyler_breakpoint"));
772        assert!(macros.contains("depyler_trace"));
773        assert!(macros.contains("#[macro_export]"));
774        assert!(macros.contains("debug_assertions"));
775    }
776
777    // === Original tests ===
778
779    #[test]
780    fn test_source_mapping() {
781        let mut generator = DebugInfoGenerator::new(
782            PathBuf::from("test.py"),
783            PathBuf::from("test.rs"),
784            DebugLevel::Full,
785        );
786
787        generator.add_mapping(10, 0, Some("test_func".to_string()));
788        generator.new_line();
789        generator.add_mapping(11, 4, None);
790
791        assert_eq!(generator.source_map().mappings.len(), 2);
792        assert_eq!(generator.source_map().mappings[0].python_line, 10);
793        assert_eq!(generator.source_map().mappings[0].rust_line, 1);
794        assert_eq!(generator.source_map().mappings[1].rust_line, 2);
795    }
796
797    #[test]
798    fn test_debug_print_generation() {
799        let generator = DebugInfoGenerator::new(
800            PathBuf::from("test.py"),
801            PathBuf::from("test.rs"),
802            DebugLevel::Full,
803        );
804
805        let int_debug = generator.generate_debug_print("x", &Type::Int);
806        assert!(int_debug.contains("eprintln!"));
807        assert!(int_debug.contains("x = {}"));
808
809        let vec_debug = generator.generate_debug_print("items", &Type::List(Box::new(Type::Int)));
810        assert!(vec_debug.contains("{:?}"));
811    }
812
813    #[test]
814    fn test_debugger_scripts() {
815        let source_map = SourceMap {
816            source_file: PathBuf::from("test.py"),
817            target_file: PathBuf::from("test.rs"),
818            mappings: vec![],
819            function_map: vec![(
820                "test_func".to_string(),
821                FunctionMapping {
822                    python_name: "test_func".to_string(),
823                    rust_name: "test_func".to_string(),
824                    python_start_line: 1,
825                    python_end_line: 5,
826                    rust_start_line: 10,
827                    rust_end_line: 20,
828                },
829            )]
830            .into_iter()
831            .collect(),
832        };
833
834        let gdb_integration = DebuggerIntegration::new(DebuggerType::Gdb);
835        let gdb_script = gdb_integration.generate_init_script(&source_map);
836        assert!(gdb_script.contains("break test_func"));
837
838        let lldb_integration = DebuggerIntegration::new(DebuggerType::Lldb);
839        let lldb_script = lldb_integration.generate_init_script(&source_map);
840        assert!(lldb_script.contains("breakpoint set --name test_func"));
841    }
842}