1mod utils;
2
3#[cfg(test)]
4mod tests;
5
6use depyler_core::DepylerPipeline;
7use serde::{Deserialize, Serialize};
8use wasm_bindgen::prelude::*;
9
10pub fn set_panic_hook() {
14 #[cfg(feature = "console_error_panic_hook")]
15 console_error_panic_hook::set_once();
16}
17
18#[wasm_bindgen]
19extern "C" {
20 fn alert(s: &str);
21
22 #[wasm_bindgen(js_namespace = console)]
23 fn log(s: &str);
24
25 #[wasm_bindgen(js_namespace = console, js_name = log)]
26 fn log_u32(a: u32);
27
28 #[wasm_bindgen(js_namespace = console, js_name = log)]
29 fn log_many(a: &str, b: &str);
30}
31
32macro_rules! console_log {
33 ($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
34}
35
36#[wasm_bindgen]
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct WasmTranspileOptions {
57 #[wasm_bindgen(skip)]
58 pub verify: bool,
59 #[wasm_bindgen(skip)]
60 pub optimize: bool,
61 #[wasm_bindgen(skip)]
62 pub emit_docs: bool,
63 #[wasm_bindgen(skip)]
64 pub target_version: String,
65}
66
67impl Default for WasmTranspileOptions {
68 fn default() -> Self {
69 Self {
70 verify: true,
71 optimize: true,
72 emit_docs: false,
73 target_version: "1.83".to_string(),
74 }
75 }
76}
77
78#[wasm_bindgen]
79impl WasmTranspileOptions {
80 #[wasm_bindgen(constructor)]
81 pub fn new() -> WasmTranspileOptions {
82 WasmTranspileOptions::default()
83 }
84
85 #[wasm_bindgen(getter)]
86 pub fn verify(&self) -> bool {
87 self.verify
88 }
89
90 #[wasm_bindgen(setter)]
91 pub fn set_verify(&mut self, verify: bool) {
92 self.verify = verify;
93 }
94
95 #[wasm_bindgen(getter)]
96 pub fn optimize(&self) -> bool {
97 self.optimize
98 }
99
100 #[wasm_bindgen(setter)]
101 pub fn set_optimize(&mut self, optimize: bool) {
102 self.optimize = optimize;
103 }
104
105 #[wasm_bindgen(getter)]
106 pub fn emit_docs(&self) -> bool {
107 self.emit_docs
108 }
109
110 #[wasm_bindgen(setter)]
111 pub fn set_emit_docs(&mut self, emit_docs: bool) {
112 self.emit_docs = emit_docs;
113 }
114
115 #[wasm_bindgen(getter)]
116 pub fn target_version(&self) -> String {
117 self.target_version.clone()
118 }
119
120 #[wasm_bindgen(setter)]
121 pub fn set_target_version(&mut self, target_version: String) {
122 self.target_version = target_version;
123 }
124}
125
126#[wasm_bindgen]
153#[derive(Debug, Clone, Serialize, Deserialize)]
154pub struct WasmTranspileResult {
155 #[wasm_bindgen(skip)]
156 pub success: bool,
157 #[wasm_bindgen(skip)]
158 pub rust_code: String,
159 #[wasm_bindgen(skip)]
160 pub errors: Vec<String>,
161 #[wasm_bindgen(skip)]
162 pub warnings: Vec<String>,
163 #[wasm_bindgen(skip)]
164 pub transpile_time_ms: f64,
165 #[wasm_bindgen(skip)]
166 pub memory_usage_mb: f64,
167 #[wasm_bindgen(skip)]
168 pub energy_estimate: WasmEnergyEstimate,
169 #[wasm_bindgen(skip)]
170 pub quality_metrics: WasmQualityMetrics,
171}
172
173#[wasm_bindgen]
174impl WasmTranspileResult {
175 #[wasm_bindgen(getter)]
176 pub fn success(&self) -> bool {
177 self.success
178 }
179
180 #[wasm_bindgen(getter)]
181 pub fn rust_code(&self) -> String {
182 self.rust_code.clone()
183 }
184
185 #[wasm_bindgen(getter)]
186 pub fn errors(&self) -> Vec<String> {
187 self.errors.clone()
188 }
189
190 #[wasm_bindgen(getter)]
191 pub fn transpile_time_ms(&self) -> f64 {
192 self.transpile_time_ms
193 }
194
195 #[wasm_bindgen(getter)]
196 pub fn warnings(&self) -> Vec<String> {
197 self.warnings.clone()
198 }
199
200 #[wasm_bindgen(getter)]
201 pub fn memory_usage_mb(&self) -> f64 {
202 self.memory_usage_mb
203 }
204
205 #[wasm_bindgen(getter)]
206 pub fn energy_estimate(&self) -> WasmEnergyEstimate {
207 self.energy_estimate.clone()
208 }
209
210 #[wasm_bindgen(getter)]
211 pub fn quality_metrics(&self) -> WasmQualityMetrics {
212 self.quality_metrics.clone()
213 }
214}
215
216#[wasm_bindgen]
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct WasmEnergyEstimate {
236 #[wasm_bindgen(skip)]
237 pub joules: f64,
238 #[wasm_bindgen(skip)]
239 pub watts_average: f64,
240 #[wasm_bindgen(skip)]
241 pub co2_grams: f64,
242 #[wasm_bindgen(skip)]
243 pub confidence: f64,
244}
245
246#[wasm_bindgen]
247impl WasmEnergyEstimate {
248 #[wasm_bindgen(getter)]
249 pub fn joules(&self) -> f64 {
250 self.joules
251 }
252
253 #[wasm_bindgen(getter)]
254 pub fn watts_average(&self) -> f64 {
255 self.watts_average
256 }
257
258 #[wasm_bindgen(getter)]
259 pub fn co2_grams(&self) -> f64 {
260 self.co2_grams
261 }
262
263 #[wasm_bindgen(getter)]
264 pub fn confidence(&self) -> f64 {
265 self.confidence
266 }
267}
268
269#[wasm_bindgen]
288#[derive(Debug, Clone, Serialize, Deserialize)]
289pub struct WasmQualityMetrics {
290 #[wasm_bindgen(skip)]
291 pub pmat_score: f64,
292 #[wasm_bindgen(skip)]
293 pub productivity: f64,
294 #[wasm_bindgen(skip)]
295 pub maintainability: f64,
296 #[wasm_bindgen(skip)]
297 pub accessibility: f64,
298 #[wasm_bindgen(skip)]
299 pub testability: f64,
300 #[wasm_bindgen(skip)]
301 pub code_complexity: i32,
302 #[wasm_bindgen(skip)]
303 pub cyclomatic_complexity: i32,
304}
305
306#[wasm_bindgen]
307impl WasmQualityMetrics {
308 #[wasm_bindgen(getter)]
309 pub fn pmat_score(&self) -> f64 {
310 self.pmat_score
311 }
312
313 #[wasm_bindgen(getter)]
314 pub fn productivity(&self) -> f64 {
315 self.productivity
316 }
317
318 #[wasm_bindgen(getter)]
319 pub fn maintainability(&self) -> f64 {
320 self.maintainability
321 }
322
323 #[wasm_bindgen(getter)]
324 pub fn accessibility(&self) -> f64 {
325 self.accessibility
326 }
327
328 #[wasm_bindgen(getter)]
329 pub fn testability(&self) -> f64 {
330 self.testability
331 }
332
333 #[wasm_bindgen(getter)]
334 pub fn code_complexity(&self) -> i32 {
335 self.code_complexity
336 }
337
338 #[wasm_bindgen(getter)]
339 pub fn cyclomatic_complexity(&self) -> i32 {
340 self.cyclomatic_complexity
341 }
342}
343
344#[wasm_bindgen]
381pub struct DepylerWasm {
382 initialized: bool,
383}
384
385#[wasm_bindgen]
386impl DepylerWasm {
387 #[wasm_bindgen(constructor)]
388 pub fn new() -> DepylerWasm {
389 set_panic_hook();
390 console_log!("Depyler WASM initialized");
391
392 DepylerWasm { initialized: true }
393 }
394
395 #[wasm_bindgen]
422 pub fn transpile(
423 &self,
424 python_code: &str,
425 options: &WasmTranspileOptions,
426 ) -> Result<WasmTranspileResult, JsValue> {
427 if !self.initialized {
428 return Err(JsValue::from_str("DepylerWasm not initialized"));
429 }
430
431 let start_time = web_sys::window()
432 .and_then(|w| w.performance())
433 .map(|p| p.now())
434 .unwrap_or(0.0);
435
436 let mem_before = get_memory_usage();
437
438 let mut pipeline = DepylerPipeline::new();
440 if options.verify {
441 pipeline = pipeline.with_verification();
442 }
443
444 let result = match pipeline.transpile(python_code) {
446 Ok(rust_code) => {
447 let transpile_time = web_sys::window()
448 .and_then(|w| w.performance())
449 .map(|p| p.now() - start_time)
450 .unwrap_or(0.0);
451
452 let mem_after = get_memory_usage();
453 let memory_usage = (mem_after - mem_before).max(0.0);
454
455 let energy_estimate = calculate_energy_estimate(transpile_time, memory_usage);
457
458 let quality_metrics = calculate_quality_metrics(python_code, &rust_code);
460
461 WasmTranspileResult {
462 success: true,
463 rust_code,
464 errors: vec![],
465 warnings: vec![],
466 transpile_time_ms: transpile_time,
467 memory_usage_mb: memory_usage,
468 energy_estimate,
469 quality_metrics,
470 }
471 }
472 Err(e) => {
473 let transpile_time = web_sys::window()
474 .and_then(|w| w.performance())
475 .map(|p| p.now() - start_time)
476 .unwrap_or(0.0);
477
478 WasmTranspileResult {
479 success: false,
480 rust_code: String::new(),
481 errors: vec![e.to_string()],
482 warnings: vec![],
483 transpile_time_ms: transpile_time,
484 memory_usage_mb: 0.0,
485 energy_estimate: WasmEnergyEstimate {
486 joules: 0.0,
487 watts_average: 0.0,
488 co2_grams: 0.0,
489 confidence: 0.0,
490 },
491 quality_metrics: WasmQualityMetrics {
492 pmat_score: 0.0,
493 productivity: 0.0,
494 maintainability: 0.0,
495 accessibility: 0.0,
496 testability: 0.0,
497 code_complexity: 0,
498 cyclomatic_complexity: 0,
499 },
500 }
501 }
502 };
503
504 Ok(result)
505 }
506
507 #[wasm_bindgen]
539 pub fn analyze_code(&self, python_code: &str) -> Result<JsValue, JsValue> {
540 if !self.initialized {
541 return Err(JsValue::from_str("DepylerWasm not initialized"));
542 }
543
544 let pipeline = DepylerPipeline::new();
545
546 let _hir = match pipeline.parse_to_hir(python_code) {
548 Ok(hir) => hir,
549 Err(e) => return Err(JsValue::from_str(&format!("Parse error: {e}"))),
550 };
551
552 let analysis = perform_static_analysis(python_code);
554
555 serde_wasm_bindgen::to_value(&analysis).map_err(|e| JsValue::from_str(&e.to_string()))
557 }
558
559 #[wasm_bindgen]
560 pub fn get_version(&self) -> String {
561 env!("CARGO_PKG_VERSION").to_string()
562 }
563
564 #[wasm_bindgen]
593 pub fn benchmark(&self, python_code: &str, iterations: u32) -> Result<JsValue, JsValue> {
594 if !self.initialized {
595 return Err(JsValue::from_str("DepylerWasm not initialized"));
596 }
597
598 let mut results = Vec::new();
599 let options = WasmTranspileOptions::default();
600
601 for _ in 0..iterations {
602 let result = self.transpile(python_code, &options)?;
603 results.push(result.transpile_time_ms);
604 }
605
606 let benchmark_result = BenchmarkResult {
607 iterations,
608 times_ms: results.clone(),
609 min_ms: results.iter().fold(f64::INFINITY, |a, &b| a.min(b)),
610 max_ms: results.iter().fold(f64::NEG_INFINITY, |a, &b| a.max(b)),
611 mean_ms: results.iter().sum::<f64>() / results.len() as f64,
612 median_ms: {
613 let mut sorted = results.clone();
614 sorted.sort_by(|a, b| a.partial_cmp(b).unwrap());
615 sorted[sorted.len() / 2]
616 },
617 std_dev_ms: {
618 let mean = results.iter().sum::<f64>() / results.len() as f64;
619 let variance =
620 results.iter().map(|&x| (x - mean).powi(2)).sum::<f64>() / results.len() as f64;
621 variance.sqrt()
622 },
623 };
624
625 serde_wasm_bindgen::to_value(&benchmark_result)
626 .map_err(|e| JsValue::from_str(&e.to_string()))
627 }
628}
629
630#[derive(Debug, Clone, Serialize, Deserialize)]
631pub struct BenchmarkResult {
632 pub iterations: u32,
633 pub times_ms: Vec<f64>,
634 pub min_ms: f64,
635 pub max_ms: f64,
636 pub mean_ms: f64,
637 pub median_ms: f64,
638 pub std_dev_ms: f64,
639}
640
641#[derive(Debug, Clone, Serialize, Deserialize)]
642pub struct StaticAnalysis {
643 pub complexity: i32,
644 pub cyclomatic_complexity: i32,
645 pub functions: Vec<FunctionInfo>,
646 pub imports: Vec<String>,
647 pub suggestions: Vec<OptimizationSuggestion>,
648 pub anti_patterns: Vec<AntiPattern>,
649}
650
651#[derive(Debug, Clone, Serialize, Deserialize)]
652pub struct FunctionInfo {
653 pub name: String,
654 pub line_start: u32,
655 pub line_end: u32,
656 pub complexity: i32,
657 pub parameters: Vec<String>,
658 pub return_type: Option<String>,
659}
660
661#[derive(Debug, Clone, Serialize, Deserialize)]
662pub struct OptimizationSuggestion {
663 pub line: u32,
664 pub column: u32,
665 pub message: String,
666 pub suggestion_type: String,
667 pub confidence: f64,
668}
669
670#[derive(Debug, Clone, Serialize, Deserialize)]
671pub struct AntiPattern {
672 pub line: u32,
673 pub column: u32,
674 pub pattern: String,
675 pub description: String,
676 pub severity: String,
677}
678
679fn get_memory_usage() -> f64 {
680 0.0
683}
684
685fn calculate_energy_estimate(execution_ms: f64, memory_mb: f64) -> WasmEnergyEstimate {
686 let cpu_joules_per_ms = 0.001_f64; let mem_joules_per_mb = 0.0002_f64;
689 let baseline_watts = 1.0_f64;
690
691 let cpu_energy = execution_ms * cpu_joules_per_ms;
692 let mem_energy = memory_mb * mem_joules_per_mb;
693 let total_joules = cpu_energy + mem_energy;
694
695 let time_weight = (execution_ms / 100.0).min(1.0) * 0.7;
697 let mem_weight = (memory_mb / 10.0).min(1.0) * 0.3;
698 let confidence = time_weight + mem_weight;
699
700 WasmEnergyEstimate {
701 joules: total_joules,
702 watts_average: baseline_watts,
703 co2_grams: total_joules * 0.475, confidence,
705 }
706}
707
708fn calculate_quality_metrics(python_code: &str, rust_code: &str) -> WasmQualityMetrics {
709 let python_lines = python_code.lines().count();
711 let rust_lines = rust_code.lines().count();
712
713 let complexity = calculate_code_complexity(python_code);
715 let cyclomatic_complexity = calculate_cyclomatic_complexity(python_code);
716
717 let productivity = calculate_productivity_score(python_lines, rust_lines);
719 let maintainability = calculate_maintainability_score(rust_code);
720 let accessibility = 0.85; let testability = calculate_testability_score(rust_code);
722
723 let pmat_score = (productivity + maintainability + accessibility + testability) / 4.0;
724
725 WasmQualityMetrics {
726 pmat_score,
727 productivity,
728 maintainability,
729 accessibility,
730 testability,
731 code_complexity: complexity,
732 cyclomatic_complexity,
733 }
734}
735
736fn calculate_code_complexity(code: &str) -> i32 {
737 let mut complexity = 1; for line in code.lines() {
740 if line.trim_start().starts_with("if ")
741 || line.trim_start().starts_with("elif ")
742 || line.trim_start().starts_with("while ")
743 || line.trim_start().starts_with("for ")
744 || line.trim_start().starts_with("try:")
745 || line.trim_start().starts_with("except ")
746 {
747 complexity += 1;
748 }
749 }
750
751 complexity
752}
753
754fn calculate_cyclomatic_complexity(code: &str) -> i32 {
755 let mut complexity = 1;
756
757 for line in code.lines() {
759 let trimmed = line.trim();
760 if trimmed.starts_with("if ")
761 || trimmed.starts_with("elif ")
762 || trimmed.starts_with("while ")
763 || trimmed.starts_with("for ")
764 || trimmed.contains(" and ")
765 || trimmed.contains(" or ")
766 {
767 complexity += 1;
768 }
769 }
770
771 complexity
772}
773
774fn calculate_productivity_score(python_lines: usize, rust_lines: usize) -> f64 {
775 if rust_lines == 0 {
777 return 0.0;
778 }
779
780 let ratio = python_lines as f64 / rust_lines as f64;
781 if ratio > 0.5 && ratio < 2.0 {
783 0.9
784 } else if ratio >= 2.0 {
785 0.7
786 } else {
787 0.5
788 }
789}
790
791fn calculate_maintainability_score(rust_code: &str) -> f64 {
792 let mut score = 0.8_f64; if rust_code.contains("/// ") {
796 score += 0.1; }
798 if rust_code.contains("#[test]") {
799 score += 0.1; }
801 if rust_code.contains("Result<") {
802 score += 0.05; }
804
805 score.min(1.0_f64)
806}
807
808fn calculate_testability_score(rust_code: &str) -> f64 {
809 let mut score = 0.7_f64; if rust_code.contains("pub fn ") {
812 score += 0.1; }
814 if rust_code.contains("impl ") {
815 score += 0.1; }
817 if rust_code.contains("#[cfg(test)]") {
818 score += 0.1; }
820
821 score.min(1.0_f64)
822}
823
824fn perform_static_analysis(python_code: &str) -> StaticAnalysis {
825 let lines: Vec<&str> = python_code.lines().collect();
826 let mut functions = Vec::new();
827 let mut suggestions = Vec::new();
828 let mut anti_patterns = Vec::new();
829 let mut imports = Vec::new();
830
831 for (line_num, line) in lines.iter().enumerate() {
833 let line_num = line_num as u32 + 1;
834 let trimmed = line.trim();
835
836 if trimmed.starts_with("def ") {
838 if let Some(func_name) = extract_function_name(trimmed) {
839 functions.push(FunctionInfo {
840 name: func_name,
841 line_start: line_num,
842 line_end: line_num, complexity: 1,
844 parameters: vec![], return_type: None,
846 });
847 }
848 }
849
850 if trimmed.starts_with("import ") || trimmed.starts_with("from ") {
852 imports.push(trimmed.to_string());
853 }
854
855 if trimmed.contains("eval(") {
857 anti_patterns.push(AntiPattern {
858 line: line_num,
859 column: line.find("eval(").unwrap_or(0) as u32,
860 pattern: "eval()".to_string(),
861 description: "Using eval() is dangerous and hard to optimize".to_string(),
862 severity: "high".to_string(),
863 });
864 }
865
866 if trimmed.contains("range(len(") {
868 suggestions.push(OptimizationSuggestion {
869 line: line_num,
870 column: line.find("range(len(").unwrap_or(0) as u32,
871 message: "Consider using enumerate() instead of range(len())".to_string(),
872 suggestion_type: "performance".to_string(),
873 confidence: 0.9,
874 });
875 }
876 }
877
878 StaticAnalysis {
879 complexity: calculate_code_complexity(python_code),
880 cyclomatic_complexity: calculate_cyclomatic_complexity(python_code),
881 functions,
882 imports,
883 suggestions,
884 anti_patterns,
885 }
886}
887
888fn extract_function_name(line: &str) -> Option<String> {
889 if let Some(start) = line.find("def ") {
891 let after_def = &line[start + 4..];
892 if let Some(end) = after_def.find('(') {
893 return Some(after_def[..end].trim().to_string());
894 }
895 }
896 None
897}
898
899#[cfg(test)]
900mod unit_tests {
901 use super::*;
902
903 #[test]
904 fn test_wasm_transpile_options_default() {
905 let options = WasmTranspileOptions::default();
906 assert!(options.verify);
907 assert!(options.optimize);
908 assert!(!options.emit_docs);
909 assert_eq!(options.target_version, "1.83");
910 }
911
912 #[test]
913 fn test_wasm_transpile_options_new() {
914 let options = WasmTranspileOptions::new();
915 assert_eq!(options.verify, WasmTranspileOptions::default().verify);
916 }
917
918 #[test]
919 fn test_wasm_transpile_options_getters_setters() {
920 let mut options = WasmTranspileOptions::new();
921
922 assert!(options.verify());
924 options.set_verify(false);
925 assert!(!options.verify());
926
927 assert!(options.optimize());
929 options.set_optimize(false);
930 assert!(!options.optimize());
931
932 assert!(!options.emit_docs());
934 options.set_emit_docs(true);
935 assert!(options.emit_docs());
936
937 assert_eq!(options.target_version(), "1.83");
939 options.set_target_version("1.84".to_string());
940 assert_eq!(options.target_version(), "1.84");
941 }
942
943 #[test]
944 fn test_extract_function_name() {
945 assert_eq!(extract_function_name("def foo():"), Some("foo".to_string()));
946 assert_eq!(
947 extract_function_name("def bar(a, b):"),
948 Some("bar".to_string())
949 );
950 assert_eq!(
951 extract_function_name(" def baz(x: int) -> int:"),
952 Some("baz".to_string())
953 );
954 assert_eq!(extract_function_name("class Foo:"), None);
955 assert_eq!(
957 extract_function_name("# def commented():"),
958 Some("commented".to_string())
959 );
960 }
961
962 #[test]
963 fn test_calculate_code_complexity() {
964 let simple = "def foo(): return 42";
965 assert_eq!(calculate_code_complexity(simple), 1);
966
967 let with_if = "def foo():\n if x > 0:\n return x";
968 assert_eq!(calculate_code_complexity(with_if), 2);
969
970 let complex = "def foo():\n if a:\n pass\n elif b:\n pass\n while c:\n pass";
971 assert_eq!(calculate_code_complexity(complex), 4);
972 }
973
974 #[test]
975 fn test_calculate_cyclomatic_complexity() {
976 let simple = "def foo(): return 42";
977 assert_eq!(calculate_cyclomatic_complexity(simple), 1);
978
979 let with_logic = "if a and b or c: pass";
980 assert_eq!(calculate_cyclomatic_complexity(with_logic), 2);
983
984 let nested = "if a:\n if b:\n pass\n elif c:\n pass";
985 assert!(calculate_cyclomatic_complexity(nested) >= 3);
986 }
987
988 #[test]
989 fn test_calculate_productivity_score() {
990 assert_eq!(calculate_productivity_score(10, 10), 0.9); assert_eq!(calculate_productivity_score(10, 5), 0.7); assert_eq!(calculate_productivity_score(10, 30), 0.5); assert_eq!(calculate_productivity_score(10, 0), 0.0); }
995
996 #[test]
997 fn test_calculate_maintainability_score() {
998 let basic = "fn foo() {}";
999 assert!(calculate_maintainability_score(basic) >= 0.8);
1000
1001 let with_docs = "/// Documentation\nfn foo() {}";
1002 assert!(
1003 calculate_maintainability_score(with_docs) > calculate_maintainability_score(basic)
1004 );
1005
1006 let with_tests = "#[test]\nfn test_foo() {}";
1007 assert!(
1008 calculate_maintainability_score(with_tests) > calculate_maintainability_score(basic)
1009 );
1010
1011 let with_result = "fn foo() -> Result<i32, Error> {}";
1012 assert!(
1013 calculate_maintainability_score(with_result) > calculate_maintainability_score(basic)
1014 );
1015 }
1016
1017 #[test]
1018 fn test_calculate_testability_score() {
1019 let private = "fn foo() {}";
1020 assert!(calculate_testability_score(private) >= 0.7);
1021
1022 let public = "pub fn foo() {}";
1023 assert!(calculate_testability_score(public) > calculate_testability_score(private));
1024
1025 let with_impl = "impl Foo { pub fn bar() {} }";
1026 assert!(calculate_testability_score(with_impl) > calculate_testability_score(private));
1027
1028 let with_test_mod = "#[cfg(test)]\nmod tests {}";
1029 assert!(calculate_testability_score(with_test_mod) > calculate_testability_score(private));
1030 }
1031
1032 #[test]
1033 fn test_energy_estimate_calculation() {
1034 let estimate = calculate_energy_estimate(100.0, 10.0);
1035 assert!(estimate.joules > 0.0);
1036 assert!(estimate.watts_average > 0.0);
1037 assert!(estimate.co2_grams > 0.0);
1038 assert!(estimate.confidence >= 0.0 && estimate.confidence <= 1.0);
1039
1040 let zero_estimate = calculate_energy_estimate(0.0, 0.0);
1042 assert_eq!(zero_estimate.joules, 0.0);
1043 assert_eq!(zero_estimate.confidence, 0.0);
1044 }
1045
1046 #[test]
1047 fn test_static_analysis() {
1048 let code = r#"
1049def test_func(x, y):
1050 import math
1051 eval("dangerous")
1052 for i in range(len(items)):
1053 pass
1054"#;
1055
1056 let analysis = perform_static_analysis(code);
1057
1058 assert_eq!(analysis.functions.len(), 1);
1059 assert_eq!(analysis.functions[0].name, "test_func");
1060 assert_eq!(analysis.imports.len(), 1);
1061 assert!(!analysis.anti_patterns.is_empty());
1062 assert!(!analysis.suggestions.is_empty());
1063 }
1064}
1065
1066impl Default for DepylerWasm {
1067 fn default() -> Self {
1068 Self::new()
1069 }
1070}
1071
1072pub type PlaygroundEngine = DepylerWasm;
1074
1075#[wasm_bindgen(start)]
1077pub fn main() {
1078 set_panic_hook();
1079 console_log!("Depyler WASM module loaded successfully");
1080}
1081
1082#[allow(dead_code)]
1084fn calculate_avg_complexity(hir: &depyler_core::hir::HirModule) -> f64 {
1085 if hir.functions.is_empty() {
1086 return 0.0;
1087 }
1088
1089 let total_complexity: u32 = hir
1090 .functions
1091 .iter()
1092 .map(|f| depyler_analyzer::calculate_cyclomatic(&f.body))
1093 .sum();
1094
1095 total_complexity as f64 / hir.functions.len() as f64
1096}