1use serde_json::Value;
4use std::collections::HashMap;
5use std::error::Error;
6
7pub fn generate_direct_html(json_data: &HashMap<String, Value>) -> Result<String, Box<dyn Error>> {
9 tracing::info!("🎨 Generating enhanced HTML with embedded JSON data...");
10
11 if json_data.is_empty() {
13 return Err("No JSON data provided for HTML generation".into());
14 }
15
16 for (key, value) in json_data {
18 tracing::info!(
19 "📊 Found data: {} ({} bytes)",
20 key,
21 serde_json::to_string(value).unwrap_or_default().len()
22 );
23 }
24
25 let transformed_data = transform_json_data_structure(json_data)?;
27
28 let safety_risk_data = generate_safety_risk_data_from_json(&transformed_data)?;
30
31 let json_data_str = serde_json::to_string(&transformed_data)
33 .map_err(|e| format!("Failed to serialize JSON data: {e}"))?;
34
35 tracing::info!(
37 "📊 JSON data serialized: {} characters",
38 json_data_str.len()
39 );
40 if let Some(memory_analysis) = transformed_data.get("memory_analysis") {
41 if let Some(allocations) = memory_analysis.get("allocations") {
42 if let Some(allocs_array) = allocations.as_array() {
43 tracing::info!(
44 "📊 Memory analysis allocations: {} items",
45 allocs_array.len()
46 );
47 }
48 }
49 }
50
51 if let Some(unsafe_ffi_data) = json_data.get("basic_usage_snapshot_unsafe_ffi") {
53 if let Some(summary) = unsafe_ffi_data.get("summary") {
54 tracing::info!("📊 Unsafe/FFI Summary: {summary}");
55 }
56 }
57
58 let template_paths = [
60 "templates/clean_dashboard.html",
62 "./templates/clean_dashboard.html",
63 "../templates/clean_dashboard.html",
64 "../../templates/clean_dashboard.html",
65 ];
66
67 let css_paths = [
68 "templates/styles.css",
69 "./templates/styles.css",
70 "../templates/styles.css",
71 "../../templates/styles.css",
72 ];
73
74 let js_paths = [
75 "templates/script.js",
76 "./templates/script.js",
77 "../templates/script.js",
78 "../../templates/script.js",
79 ];
80
81 let template_content = template_paths
82 .iter()
83 .find_map(|path| std::fs::read_to_string(path).ok())
84 .ok_or("Failed to find dashboard template file in any expected location")?;
85
86 let css_content = css_paths
88 .iter()
89 .find_map(|path| std::fs::read_to_string(path).ok())
90 .ok_or("Failed to find CSS template file in any expected location")?;
91
92 let js_content = js_paths
94 .iter()
95 .find_map(|path| std::fs::read_to_string(path).ok())
96 .ok_or("Failed to find JavaScript template file in any expected location")?;
97
98 let mut html = template_content
100 .replace("{{ json_data }}", &json_data_str) .replace("{{json_data}}", &json_data_str) .replace("{{CSS_CONTENT}}", &css_content)
103 .replace("{{JS_CONTENT}}", &js_content)
104 .replace("{{DATA_PLACEHOLDER}}", &json_data_str)
105 .replace(
106 "{{\n {\n CSS_CONTENT\n }\n }",
107 &css_content,
108 ) .replace(
110 "{\n {\n CSS_CONTENT\n }\n }",
111 &css_content,
112 ); html = inject_safety_risk_data_into_html(html, &safety_risk_data)?;
116
117 tracing::info!(
118 "✅ Generated HTML with {} bytes of embedded JSON data",
119 json_data_str.len()
120 );
121
122 Ok(html)
123}
124
125fn transform_json_data_structure(
128 json_data: &HashMap<String, Value>,
129) -> Result<serde_json::Map<String, Value>, Box<dyn Error>> {
130 let mut transformed = serde_json::Map::new();
131
132 for (file_key, file_data) in json_data {
134 if file_key.contains("memory_analysis") {
136 let enhanced_memory_data = enhance_memory_analysis_data(file_data)?;
137 transformed.insert("memory_analysis".to_string(), enhanced_memory_data);
138 } else if file_key.contains("lifetime") {
139 let enhanced_lifetime_data = enhance_lifetime_data(file_data)?;
140 transformed.insert("lifetime".to_string(), enhanced_lifetime_data);
141 } else if file_key.contains("complex_types") {
142 transformed.insert("complex_types".to_string(), file_data.clone());
143 } else if file_key.contains("performance") {
144 transformed.insert("performance".to_string(), file_data.clone());
145 } else if file_key.contains("unsafe_ffi") {
146 let enhanced_ffi_data = enhance_ffi_data(file_data)?;
147 transformed.insert("unsafe_ffi".to_string(), enhanced_ffi_data);
148 transformed.insert(file_key.clone(), file_data.clone());
150 } else if file_key.contains("security_violations") {
151 transformed.insert("security_violations".to_string(), file_data.clone());
152 } else if file_key.contains("variable_relationships") {
153 transformed.insert("variable_relationships".to_string(), file_data.clone());
154 } else {
155 transformed.insert(file_key.clone(), file_data.clone());
157 }
158 }
159
160 if !transformed.contains_key("memory_analysis") {
162 transformed.insert(
163 "memory_analysis".to_string(),
164 serde_json::json!({
165 "allocations": [],
166 "stats": {
167 "total_allocations": 0,
168 "active_allocations": 0,
169 "total_memory": 0,
170 "active_memory": 0
171 }
172 }),
173 );
174 }
175
176 if !transformed.contains_key("lifetime") {
177 transformed.insert(
178 "lifetime".to_string(),
179 serde_json::json!({
180 "lifecycle_events": []
181 }),
182 );
183 }
184
185 if !transformed.contains_key("complex_types") {
186 transformed.insert(
187 "complex_types".to_string(),
188 serde_json::json!({
189 "categorized_types": {
190 "generic_types": [],
191 "collections": [],
192 "smart_pointers": [],
193 "trait_objects": []
194 },
195 "summary": {
196 "total_complex_types": 0,
197 "generic_type_count": 0
198 }
199 }),
200 );
201 }
202
203 if !transformed.contains_key("performance") {
204 transformed.insert(
205 "performance".to_string(),
206 serde_json::json!({
207 "memory_performance": {
208 "active_memory": 0,
209 "peak_memory": 0,
210 "total_allocated": 0
211 },
212 "allocation_distribution": {
213 "tiny": 0,
214 "small": 0,
215 "medium": 0,
216 "large": 0,
217 "massive": 0
218 }
219 }),
220 );
221 }
222
223 if !transformed.contains_key("unsafe_ffi") {
224 transformed.insert(
225 "unsafe_ffi".to_string(),
226 serde_json::json!({
227 "summary": {
228 "total_risk_items": 0,
229 "unsafe_count": 0,
230 "ffi_count": 0,
231 "safety_violations": 0
232 },
233 "enhanced_ffi_data": [],
234 "safety_violations": []
235 }),
236 );
237 }
238
239 if !transformed.contains_key("security_violations") {
240 transformed.insert(
241 "security_violations".to_string(),
242 serde_json::json!({
243 "metadata": {
244 "total_violations": 0
245 },
246 "violation_reports": [],
247 "security_summary": {
248 "security_analysis_summary": {
249 "total_violations": 0,
250 "severity_breakdown": {
251 "critical": 0,
252 "high": 0,
253 "medium": 0,
254 "low": 0,
255 "info": 0
256 }
257 }
258 }
259 }),
260 );
261 }
262
263 tracing::info!(
264 "🔄 Transformed data structure with keys: {:?}",
265 transformed.keys().collect::<Vec<_>>()
266 );
267
268 Ok(transformed)
269}
270
271fn enhance_memory_analysis_data(data: &Value) -> Result<Value, Box<dyn Error>> {
273 let mut enhanced = data.clone();
274
275 if let Some(allocations) = data.get("allocations").and_then(|a| a.as_array()) {
276 let fragmentation_data = analyze_memory_fragmentation(allocations);
278
279 let growth_trends = analyze_memory_growth_trends(allocations);
281
282 if let Some(obj) = enhanced.as_object_mut() {
284 obj.insert("fragmentation_analysis".to_string(), fragmentation_data);
285 obj.insert("growth_trends".to_string(), growth_trends);
286 obj.insert("visualization_ready".to_string(), serde_json::json!(true));
287 }
288 }
289
290 Ok(enhanced)
291}
292
293fn enhance_lifetime_data(data: &Value) -> Result<Value, Box<dyn Error>> {
295 let mut enhanced = data.clone();
296
297 if let Some(events) = data.get("lifecycle_events").and_then(|e| e.as_array()) {
298 let user_variables: Vec<&Value> = events
300 .iter()
301 .filter(|event| {
302 event
303 .get("var_name")
304 .and_then(|v| v.as_str())
305 .is_some_and(|s| s != "unknown")
306 && event
307 .get("type_name")
308 .and_then(|v| v.as_str())
309 .is_some_and(|s| s != "unknown")
310 })
311 .collect();
312
313 let mut variable_groups = std::collections::HashMap::new();
315 for (index, event) in user_variables.iter().enumerate() {
316 if let Some(var_name) = event.get("var_name").and_then(|v| v.as_str()) {
317 let color_index = index % 10; let color = get_progress_color(color_index);
319
320 let group = variable_groups
321 .entry(var_name.to_string())
322 .or_insert_with(|| {
323 serde_json::json!({
324 "var_name": var_name,
325 "type_name": event.get("type_name"),
326 "color": color,
327 "color_index": color_index,
328 "events": []
329 })
330 });
331
332 if let Some(events_array) = group.get_mut("events").and_then(|e| e.as_array_mut()) {
333 events_array.push((*event).clone());
334 }
335 }
336 }
337
338 let grouped_variables: Vec<Value> = variable_groups.into_values().collect();
340
341 if let Some(obj) = enhanced.as_object_mut() {
342 obj.insert(
343 "variable_groups".to_string(),
344 serde_json::json!(grouped_variables),
345 );
346 obj.insert(
347 "user_variables_count".to_string(),
348 serde_json::json!(user_variables.len()),
349 );
350 obj.insert("visualization_ready".to_string(), serde_json::json!(true));
351 }
352 }
353
354 Ok(enhanced)
355}
356
357fn enhance_ffi_data(data: &Value) -> Result<Value, Box<dyn Error>> {
359 let mut enhanced = data.clone();
360
361 let empty_vec = vec![];
362 let allocations = data
364 .get("allocations")
365 .and_then(|d| d.as_array())
366 .unwrap_or(&empty_vec);
367
368 let enhanced_data = if allocations.is_empty() {
370 data.get("enhanced_ffi_data")
371 .and_then(|d| d.as_array())
372 .unwrap_or(&empty_vec)
373 } else {
374 allocations
375 };
376
377 let boundary_events = data
378 .get("boundary_events")
379 .and_then(|d| d.as_array())
380 .unwrap_or(&empty_vec);
381
382 tracing::info!(
383 "🔍 FFI data enhancement - allocations: {}, enhanced_data: {}, boundary_events: {}",
384 allocations.len(),
385 enhanced_data.len(),
386 boundary_events.len()
387 );
388
389 let stats = calculate_ffi_statistics_from_allocations(enhanced_data, boundary_events);
391
392 let language_interactions = analyze_language_interactions(boundary_events);
394
395 let safety_analysis = analyze_safety_metrics_from_allocations(enhanced_data);
397
398 let dashboard_metrics = create_ffi_dashboard_metrics(enhanced_data, boundary_events);
400
401 let memory_hotspots = analyze_memory_hotspots(enhanced_data);
403
404 let memory_flow = analyze_cross_language_memory_flow(enhanced_data, boundary_events);
406
407 let risk_assessment = create_ffi_risk_assessment(enhanced_data);
409
410 if let Some(obj) = enhanced.as_object_mut() {
411 obj.insert("comprehensive_stats".to_string(), stats);
412 obj.insert("language_interactions".to_string(), language_interactions);
413 obj.insert("safety_analysis".to_string(), safety_analysis);
414 obj.insert("dashboard_metrics".to_string(), dashboard_metrics);
415 obj.insert("memory_hotspots".to_string(), memory_hotspots);
416 obj.insert("memory_flow".to_string(), memory_flow);
417 obj.insert("risk_assessment".to_string(), risk_assessment);
418 obj.insert("visualization_ready".to_string(), serde_json::json!(true));
419 if !allocations.is_empty() {
421 obj.insert("allocations".to_string(), serde_json::json!(allocations));
422 }
423 }
424
425 Ok(enhanced)
426}
427
428fn analyze_memory_fragmentation(allocations: &[Value]) -> Value {
430 let mut sorted_allocs: Vec<_> = allocations
431 .iter()
432 .filter_map(|alloc| {
433 let ptr_str = alloc.get("ptr")?.as_str()?;
434 let size = alloc.get("size")?.as_u64()? as usize;
435 let address = u64::from_str_radix(ptr_str.trim_start_matches("0x"), 16).ok()?;
436 Some((address, size))
437 })
438 .collect();
439
440 sorted_allocs.sort_by_key(|&(addr, _)| addr);
441
442 let mut gaps = 0;
443 let mut total_gap_size = 0u64;
444
445 for i in 1..sorted_allocs.len() {
446 let (prev_addr, prev_size) = sorted_allocs[i - 1];
447 let (curr_addr, _) = sorted_allocs[i];
448 let prev_end = prev_addr + prev_size as u64;
449
450 if curr_addr > prev_end {
451 gaps += 1;
452 total_gap_size += curr_addr - prev_end;
453 }
454 }
455
456 let total_memory: u64 = sorted_allocs.iter().map(|(_, size)| *size as u64).sum();
457 let fragmentation_score = if total_memory > 0 {
458 ((total_gap_size as f64 / (total_memory + total_gap_size) as f64) * 100.0) as u32
459 } else {
460 0
461 };
462
463 let largest_block = sorted_allocs
464 .iter()
465 .map(|(_, size)| *size)
466 .max()
467 .unwrap_or(0);
468
469 serde_json::json!({
470 "total_blocks": sorted_allocs.len(),
471 "fragmentation_score": fragmentation_score,
472 "largest_block": largest_block,
473 "gaps": gaps,
474 "total_gap_size": total_gap_size,
475 "analysis": get_fragmentation_analysis(fragmentation_score)
476 })
477}
478
479fn analyze_memory_growth_trends(allocations: &[Value]) -> Value {
481 let mut sorted_allocs: Vec<_> = allocations
482 .iter()
483 .filter_map(|alloc| {
484 let timestamp = alloc.get("timestamp_alloc")?.as_u64()?;
485 let size = alloc.get("size")?.as_u64()? as usize;
486 Some((timestamp, size))
487 })
488 .collect();
489
490 sorted_allocs.sort_by_key(|&(timestamp, _)| timestamp);
491
492 let mut cumulative_memory = 0;
493 let time_points: Vec<_> = sorted_allocs
494 .iter()
495 .enumerate()
496 .map(|(index, &(timestamp, size))| {
497 cumulative_memory += size;
498 serde_json::json!({
499 "timestamp": timestamp,
500 "memory": cumulative_memory,
501 "index": index
502 })
503 })
504 .take(100) .collect();
506
507 let peak_memory = time_points
508 .iter()
509 .filter_map(|p| p.get("memory")?.as_u64())
510 .max()
511 .unwrap_or(0);
512
513 let current_memory = time_points
514 .last()
515 .and_then(|p| p.get("memory")?.as_u64())
516 .unwrap_or(0);
517
518 let start_memory = time_points
519 .first()
520 .and_then(|p| p.get("memory")?.as_u64())
521 .unwrap_or(0);
522
523 let growth_rate = if start_memory > 0 {
524 ((current_memory as f64 - start_memory as f64) / start_memory as f64 * 100.0) as i32
525 } else {
526 0
527 };
528
529 let time_span = if time_points.len() > 1 {
530 let start_time = time_points[0]
531 .get("timestamp")
532 .and_then(|t| t.as_u64())
533 .unwrap_or(0);
534 let end_time = time_points
535 .last()
536 .and_then(|p| p.get("timestamp"))
537 .and_then(|t| t.as_u64())
538 .unwrap_or(0);
539 if end_time > start_time {
540 (end_time - start_time) / 1_000_000_000 } else {
542 1
543 }
544 } else {
545 1
546 };
547
548 let allocation_rate = if time_span > 0 {
549 allocations.len() as u64 / time_span
550 } else {
551 0
552 };
553
554 serde_json::json!({
555 "peak_memory": peak_memory,
556 "current_memory": current_memory,
557 "growth_rate": growth_rate,
558 "allocation_rate": allocation_rate,
559 "time_points": time_points,
560 "analysis": get_trend_analysis(growth_rate)
561 })
562}
563
564fn calculate_ffi_statistics_from_allocations(
566 allocations: &[Value],
567 boundary_events: &[Value],
568) -> Value {
569 let ffi_tracked_allocations = allocations
570 .iter()
571 .filter(|item| {
572 item.get("ffi_tracked")
573 .and_then(|f| f.as_bool())
574 .unwrap_or(false)
575 })
576 .count();
577
578 let non_ffi_allocations = allocations.len() - ffi_tracked_allocations;
579
580 let boundary_crossings = boundary_events.len();
581
582 let safety_violations = allocations
584 .iter()
585 .map(|item| {
586 item.get("safety_violations")
587 .and_then(|s| s.as_array())
588 .map(|arr| arr.len() as u64)
589 .unwrap_or(0)
590 })
591 .sum::<u64>();
592
593 let borrow_conflicts = allocations
595 .iter()
596 .filter(|item| {
597 if let Some(borrow_info) = item.get("borrow_info") {
598 let immutable = borrow_info
599 .get("immutable_borrows")
600 .and_then(|v| v.as_u64())
601 .unwrap_or(0);
602 let mutable = borrow_info
603 .get("mutable_borrows")
604 .and_then(|v| v.as_u64())
605 .unwrap_or(0);
606 immutable > 0 && mutable > 0
607 } else {
608 false
609 }
610 })
611 .count();
612
613 let total_clones = allocations
615 .iter()
616 .map(|item| {
617 item.get("clone_info")
618 .and_then(|c| c.get("clone_count"))
619 .and_then(|cc| cc.as_u64())
620 .unwrap_or(0)
621 })
622 .sum::<u64>();
623
624 let total_memory = allocations
625 .iter()
626 .map(|item| item.get("size").and_then(|s| s.as_u64()).unwrap_or(0))
627 .sum::<u64>();
628
629 serde_json::json!({
630 "total_allocations": allocations.len(),
631 "ffi_tracked_allocations": ffi_tracked_allocations,
632 "non_ffi_allocations": non_ffi_allocations,
633 "boundary_crossings": boundary_crossings,
634 "safety_violations": safety_violations,
635 "borrow_conflicts": borrow_conflicts,
636 "total_clones": total_clones,
637 "total_memory": total_memory
638 })
639}
640
641fn analyze_language_interactions(boundary_events: &[Value]) -> Value {
643 let mut interactions = std::collections::HashMap::new();
644
645 for event in boundary_events {
646 if let (Some(from), Some(to)) = (
647 event.get("from_context").and_then(|f| f.as_str()),
648 event.get("to_context").and_then(|t| t.as_str()),
649 ) {
650 let key = format!("{from} → {to}");
651 *interactions.entry(key).or_insert(0) += 1;
652 }
653 }
654
655 let interactions_vec: Vec<_> = interactions
656 .into_iter()
657 .map(|(interaction, count)| {
658 serde_json::json!({
659 "interaction": interaction,
660 "count": count
661 })
662 })
663 .collect();
664
665 serde_json::json!(interactions_vec)
666}
667
668fn analyze_safety_metrics_from_allocations(allocations: &[Value]) -> Value {
670 let safe_operations = allocations
671 .iter()
672 .filter(|item| {
673 item.get("safety_violations")
675 .and_then(|s| s.as_array())
676 .map(|arr| arr.is_empty())
677 .unwrap_or(true)
678 })
679 .count();
680
681 let unsafe_operations = allocations.len() - safe_operations;
682 let total_operations = allocations.len();
683
684 let safety_percentage = if total_operations > 0 {
685 (safe_operations as f64 / total_operations as f64 * 100.0) as u32
686 } else {
687 100
688 };
689
690 let with_ownership_history = allocations
692 .iter()
693 .filter(|item| {
694 item.get("ownership_history_available")
695 .and_then(|o| o.as_bool())
696 .unwrap_or(false)
697 })
698 .count();
699
700 let leaked_allocations = allocations
702 .iter()
703 .filter(|item| {
704 item.get("is_leaked")
705 .and_then(|l| l.as_bool())
706 .unwrap_or(false)
707 })
708 .count();
709
710 serde_json::json!({
711 "safe_operations": safe_operations,
712 "unsafe_operations": unsafe_operations,
713 "total_operations": total_operations,
714 "safety_percentage": safety_percentage,
715 "with_ownership_history": with_ownership_history,
716 "leaked_allocations": leaked_allocations
717 })
718}
719
720fn _analyze_safety_metrics(enhanced_data: &[Value]) -> Value {
722 analyze_safety_metrics_from_allocations(enhanced_data)
723}
724
725fn get_progress_color(index: usize) -> &'static str {
727 const COLORS: &[&str] = &[
728 "#ff6b6b", "#4ecdc4", "#45b7d1", "#96ceb4", "#feca57", "#ff9ff3", "#54a0ff", "#5f27cd",
729 "#00d2d3", "#ff9f43",
730 ];
731 COLORS[index % COLORS.len()]
732}
733
734fn get_fragmentation_analysis(score: u32) -> &'static str {
736 match score {
737 0..=9 => "Excellent memory layout with minimal fragmentation.",
738 10..=24 => "Good memory layout with low fragmentation.",
739 25..=49 => "Moderate fragmentation detected. Consider memory pool allocation.",
740 _ => "High fragmentation detected. Memory layout optimization recommended.",
741 }
742}
743
744fn get_trend_analysis(growth_rate: i32) -> &'static str {
746 match growth_rate {
747 i32::MIN..=-1 => "Memory usage is decreasing - good memory management.",
748 0..=9 => "Stable memory usage with minimal growth.",
749 10..=49 => "Moderate memory growth - monitor for potential leaks.",
750 _ => "High memory growth detected - investigate for memory leaks.",
751 }
752}
753
754fn format_memory_size(bytes: u64) -> String {
756 const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
757 let mut size = bytes as f64;
758 let mut unit_index = 0;
759
760 while size >= 1024.0 && unit_index < UNITS.len() - 1 {
761 size /= 1024.0;
762 unit_index += 1;
763 }
764
765 if unit_index == 0 {
766 format!("{bytes} {unit}", unit = UNITS[unit_index])
767 } else {
768 format!("{:.1} {unit}", size, unit = UNITS[unit_index])
769 }
770}
771
772fn calculate_risk_level(size: u64, is_unsafe: bool, is_ffi: bool) -> String {
774 if is_unsafe {
775 "HIGH".to_string()
776 } else if is_ffi && size > 1024 * 1024 {
777 "MEDIUM".to_string()
778 } else if is_ffi {
779 "LOW".to_string()
780 } else {
781 "SAFE".to_string()
782 }
783}
784
785fn create_ffi_dashboard_metrics(allocations: &[Value], boundary_events: &[Value]) -> Value {
787 let total_allocations = allocations.len();
788
789 let unsafe_allocations = allocations
791 .iter()
792 .filter(|item| {
793 item.get("safety_violations")
794 .and_then(|s| s.as_array())
795 .map(|arr| !arr.is_empty())
796 .unwrap_or(false)
797 })
798 .count();
799
800 let ffi_allocations = allocations
802 .iter()
803 .filter(|item| {
804 item.get("ffi_tracked")
805 .and_then(|f| f.as_bool())
806 .unwrap_or(false)
807 })
808 .count();
809
810 let boundary_crossings = boundary_events.len();
812
813 let safety_violations = allocations
815 .iter()
816 .map(|item| {
817 item.get("safety_violations")
818 .and_then(|s| s.as_array())
819 .map(|arr| arr.len())
820 .unwrap_or(0)
821 })
822 .sum::<usize>();
823
824 let unsafe_memory: u64 = allocations
826 .iter()
827 .filter(|item| {
828 item.get("safety_violations")
829 .and_then(|s| s.as_array())
830 .map(|arr| !arr.is_empty())
831 .unwrap_or(false)
832 })
833 .map(|item| item.get("size").and_then(|s| s.as_u64()).unwrap_or(0))
834 .sum();
835
836 let safety_score = if total_allocations > 0 {
838 ((total_allocations - unsafe_allocations) as f64 / total_allocations as f64 * 100.0) as u32
839 } else {
840 100
841 };
842
843 let smart_pointer_types = analyze_smart_pointer_types(allocations);
845
846 let borrow_metrics = analyze_borrow_checker_metrics(allocations);
848
849 serde_json::json!({
850 "unsafe_allocations": unsafe_allocations,
851 "ffi_allocations": ffi_allocations,
852 "boundary_crossings": boundary_crossings,
853 "safety_violations": safety_violations,
854 "unsafe_memory": unsafe_memory,
855 "total_allocations": total_allocations,
856 "safety_score": safety_score,
857 "unsafe_memory_formatted": format_memory_size(unsafe_memory),
858 "smart_pointer_types": smart_pointer_types,
859 "borrow_metrics": borrow_metrics
860 })
861}
862
863fn analyze_smart_pointer_types(allocations: &[Value]) -> Value {
865 let mut type_counts = std::collections::HashMap::new();
866
867 for allocation in allocations {
868 if let Some(type_name) = allocation.get("type_name").and_then(|t| t.as_str()) {
869 if type_name.contains("Arc")
870 || type_name.contains("Rc")
871 || type_name.contains("Box")
872 || type_name.contains("RefCell")
873 {
874 let short_type = if type_name.contains("Arc") {
876 "Arc"
877 } else if type_name.contains("Rc") {
878 "Rc"
879 } else if type_name.contains("Box") {
880 "Box"
881 } else if type_name.contains("RefCell") {
882 "RefCell"
883 } else {
884 "Other"
885 };
886
887 *type_counts.entry(short_type.to_string()).or_insert(0) += 1;
888 }
889 }
890 }
891
892 serde_json::json!(type_counts)
893}
894
895fn analyze_borrow_checker_metrics(allocations: &[Value]) -> Value {
897 let mut max_concurrent = 0;
898 let mut total_borrows = 0;
899 let mut conflicts = 0;
900
901 for allocation in allocations {
902 if let Some(borrow_info) = allocation.get("borrow_info") {
903 if let Some(max_concurrent_borrows) = borrow_info
904 .get("max_concurrent_borrows")
905 .and_then(|m| m.as_u64())
906 {
907 max_concurrent = max_concurrent.max(max_concurrent_borrows);
908 }
909
910 let immutable = borrow_info
911 .get("immutable_borrows")
912 .and_then(|i| i.as_u64())
913 .unwrap_or(0);
914 let mutable = borrow_info
915 .get("mutable_borrows")
916 .and_then(|m| m.as_u64())
917 .unwrap_or(0);
918
919 total_borrows += immutable + mutable;
920
921 if immutable > 0 && mutable > 0 {
923 conflicts += 1;
924 }
925 }
926 }
927
928 serde_json::json!({
929 "max_concurrent_borrows": max_concurrent,
930 "total_borrow_operations": total_borrows,
931 "borrow_conflicts": conflicts
932 })
933}
934
935fn analyze_memory_hotspots(allocations: &[Value]) -> Value {
937 let mut hotspots = Vec::new();
938
939 for allocation in allocations {
940 if let (Some(size), Some(ptr), Some(type_name)) = (
941 allocation.get("size").and_then(|s| s.as_u64()),
942 allocation.get("ptr").and_then(|p| p.as_str()),
943 allocation.get("type_name").and_then(|t| t.as_str()),
944 ) {
945 let is_unsafe = allocation
946 .get("safety_violations")
947 .and_then(|s| s.as_array())
948 .map(|arr| !arr.is_empty())
949 .unwrap_or(false);
950
951 let is_ffi = allocation
952 .get("ffi_tracked")
953 .and_then(|f| f.as_bool())
954 .unwrap_or(false);
955
956 hotspots.push(serde_json::json!({
957 "ptr": ptr,
958 "size": size,
959 "type_name": type_name,
960 "is_unsafe": is_unsafe,
961 "is_ffi": is_ffi,
962 "category": if is_unsafe { "UNSAFE" } else { "FFI" },
963 "size_formatted": format_memory_size(size),
964 "risk_level": calculate_risk_level(size, is_unsafe, is_ffi)
965 }));
966 }
967 }
968
969 hotspots.sort_by(|a, b| {
971 let size_a = a.get("size").and_then(|s| s.as_u64()).unwrap_or(0);
972 let size_b = b.get("size").and_then(|s| s.as_u64()).unwrap_or(0);
973 size_b.cmp(&size_a)
974 });
975
976 serde_json::json!(hotspots)
977}
978
979fn analyze_cross_language_memory_flow(allocations: &[Value], boundary_events: &[Value]) -> Value {
981 let rust_allocations = allocations
982 .iter()
983 .filter(|item| {
984 !item
985 .get("ffi_tracked")
986 .and_then(|f| f.as_bool())
987 .unwrap_or(false)
988 })
989 .count();
990
991 let ffi_allocations = allocations.len() - rust_allocations;
992
993 let mut rust_to_ffi = 0;
995 let mut ffi_to_rust = 0;
996
997 for event in boundary_events {
998 if let (Some(from), Some(to)) = (
999 event.get("from_context").and_then(|f| f.as_str()),
1000 event.get("to_context").and_then(|t| t.as_str()),
1001 ) {
1002 match (from, to) {
1003 ("rust", "ffi") | ("rust", "c") => rust_to_ffi += 1,
1004 ("ffi", "rust") | ("c", "rust") => ffi_to_rust += 1,
1005 _ => {}
1006 }
1007 }
1008 }
1009
1010 serde_json::json!({
1011 "rust_allocations": rust_allocations,
1012 "ffi_allocations": ffi_allocations,
1013 "rust_to_ffi_flow": rust_to_ffi,
1014 "ffi_to_rust_flow": ffi_to_rust,
1015 "total_boundary_crossings": boundary_events.len()
1016 })
1017}
1018
1019fn create_ffi_risk_assessment(allocations: &[Value]) -> Value {
1021 let mut risk_items = Vec::new();
1022
1023 for allocation in allocations {
1024 let empty_vec = vec![];
1025 let safety_violations = allocation
1026 .get("safety_violations")
1027 .and_then(|s| s.as_array())
1028 .unwrap_or(&empty_vec);
1029
1030 if !safety_violations.is_empty() {
1031 for violation in safety_violations {
1032 if let Some(violation_str) = violation.as_str() {
1033 risk_items.push(serde_json::json!({
1034 "type": "safety_violation",
1035 "description": violation_str,
1036 "severity": get_violation_severity(violation_str),
1037 "ptr": allocation.get("ptr"),
1038 "size": allocation.get("size")
1039 }));
1040 }
1041 }
1042 }
1043
1044 if let Some(borrow_info) = allocation.get("borrow_info") {
1046 let immutable = borrow_info
1047 .get("immutable_borrows")
1048 .and_then(|v| v.as_u64())
1049 .unwrap_or(0);
1050 let mutable = borrow_info
1051 .get("mutable_borrows")
1052 .and_then(|v| v.as_u64())
1053 .unwrap_or(0);
1054
1055 if immutable > 0 && mutable > 0 {
1056 risk_items.push(serde_json::json!({
1057 "type": "borrow_conflict",
1058 "description": "Concurrent immutable and mutable borrows detected",
1059 "severity": "medium",
1060 "ptr": allocation.get("ptr"),
1061 "immutable_borrows": immutable,
1062 "mutable_borrows": mutable
1063 }));
1064 }
1065 }
1066 }
1067
1068 let critical_risks = risk_items
1070 .iter()
1071 .filter(|r| r.get("severity").and_then(|s| s.as_str()) == Some("critical"))
1072 .count();
1073 let high_risks = risk_items
1074 .iter()
1075 .filter(|r| r.get("severity").and_then(|s| s.as_str()) == Some("high"))
1076 .count();
1077 let medium_risks = risk_items
1078 .iter()
1079 .filter(|r| r.get("severity").and_then(|s| s.as_str()) == Some("medium"))
1080 .count();
1081 let low_risks = risk_items
1082 .iter()
1083 .filter(|r| r.get("severity").and_then(|s| s.as_str()) == Some("low"))
1084 .count();
1085
1086 serde_json::json!({
1087 "risk_items": risk_items,
1088 "summary": {
1089 "total_risks": risk_items.len(),
1090 "critical": critical_risks,
1091 "high": high_risks,
1092 "medium": medium_risks,
1093 "low": low_risks
1094 }
1095 })
1096}
1097
1098fn get_violation_severity(violation: &str) -> &'static str {
1100 match violation.to_lowercase().as_str() {
1101 v if v.contains("double free") || v.contains("use after free") => "critical",
1102 v if v.contains("invalid free") || v.contains("buffer overflow") => "high",
1103 v if v.contains("memory leak") || v.contains("uninitialized") => "medium",
1104 _ => "low",
1105 }
1106}
1107
1108fn generate_safety_risk_data_from_json(
1110 transformed_data: &serde_json::Map<String, Value>,
1111) -> Result<String, Box<dyn Error>> {
1112 let mut safety_risks = Vec::new();
1113
1114 if let Some(memory_analysis) = transformed_data.get("memory_analysis") {
1116 if let Some(allocations) = memory_analysis
1117 .get("allocations")
1118 .and_then(|a| a.as_array())
1119 {
1120 for allocation in allocations {
1121 if let Some(size) = allocation.get("size").and_then(|s| s.as_u64()) {
1125 if size > 1024 * 1024 {
1126 safety_risks.push(serde_json::json!({
1128 "location": format!("{}::{}",
1129 allocation.get("scope_name").and_then(|s| s.as_str()).unwrap_or("unknown"),
1130 allocation.get("var_name").and_then(|s| s.as_str()).unwrap_or("unnamed")),
1131 "operation": "Large Memory Allocation",
1132 "risk_level": "Medium",
1133 "description": format!("Large allocation of {} bytes may indicate unsafe buffer operations", size)
1134 }));
1135 }
1136 }
1137
1138 if let Some(is_leaked) = allocation.get("is_leaked").and_then(|l| l.as_bool()) {
1140 if is_leaked {
1141 safety_risks.push(serde_json::json!({
1142 "location": format!("{}::{}",
1143 allocation.get("scope_name").and_then(|s| s.as_str()).unwrap_or("unknown"),
1144 allocation.get("var_name").and_then(|s| s.as_str()).unwrap_or("unnamed")),
1145 "operation": "Memory Leak",
1146 "risk_level": "High",
1147 "description": "Memory leak detected - potential unsafe memory management"
1148 }));
1149 }
1150 }
1151
1152 if let Some(borrow_count) = allocation.get("borrow_count").and_then(|b| b.as_u64())
1154 {
1155 if borrow_count > 10 {
1156 safety_risks.push(serde_json::json!({
1157 "location": format!("{}::{}",
1158 allocation.get("scope_name").and_then(|s| s.as_str()).unwrap_or("unknown"),
1159 allocation.get("var_name").and_then(|s| s.as_str()).unwrap_or("unnamed")),
1160 "operation": "High Borrow Count",
1161 "risk_level": "Medium",
1162 "description": format!("High borrow count ({}) may indicate unsafe sharing patterns", borrow_count)
1163 }));
1164 }
1165 }
1166
1167 if let Some(type_name) = allocation.get("type_name").and_then(|t| t.as_str()) {
1169 if type_name.contains("*mut") || type_name.contains("*const") {
1170 safety_risks.push(serde_json::json!({
1171 "location": format!("{}::{}",
1172 allocation.get("scope_name").and_then(|s| s.as_str()).unwrap_or("unknown"),
1173 allocation.get("var_name").and_then(|s| s.as_str()).unwrap_or("unnamed")),
1174 "operation": "Raw Pointer Usage",
1175 "risk_level": "High",
1176 "description": format!("Raw pointer type '{}' requires unsafe operations", type_name)
1177 }));
1178 }
1179
1180 if type_name.contains("CString")
1182 || type_name.contains("CStr")
1183 || type_name.contains("c_void")
1184 || type_name.contains("extern")
1185 {
1186 safety_risks.push(serde_json::json!({
1187 "location": format!("{}::{}",
1188 allocation.get("scope_name").and_then(|s| s.as_str()).unwrap_or("unknown"),
1189 allocation.get("var_name").and_then(|s| s.as_str()).unwrap_or("unnamed")),
1190 "operation": "FFI Boundary Crossing",
1191 "risk_level": "Medium",
1192 "description": format!("FFI type '{}' crosses safety boundaries", type_name)
1193 }));
1194 }
1195 }
1196
1197 if let Some(lifetime_ms) = allocation.get("lifetime_ms").and_then(|l| l.as_u64()) {
1199 if lifetime_ms < 1 {
1200 safety_risks.push(serde_json::json!({
1202 "location": format!("{}::{}",
1203 allocation.get("scope_name").and_then(|s| s.as_str()).unwrap_or("unknown"),
1204 allocation.get("var_name").and_then(|s| s.as_str()).unwrap_or("unnamed")),
1205 "operation": "Short-lived Allocation",
1206 "risk_level": "Low",
1207 "description": format!("Very short lifetime ({}ms) may indicate unsafe temporary operations", lifetime_ms)
1208 }));
1209 }
1210 }
1211 }
1212 }
1213 }
1214
1215 if let Some(unsafe_ffi) = transformed_data.get("unsafe_ffi") {
1217 if let Some(safety_violations) = unsafe_ffi
1218 .get("safety_violations")
1219 .and_then(|sv| sv.as_array())
1220 {
1221 for violation in safety_violations {
1222 if let Some(violation_type) =
1223 violation.get("violation_type").and_then(|vt| vt.as_str())
1224 {
1225 let severity = get_violation_severity(violation_type);
1226 let risk_level = match severity {
1227 "critical" => "High",
1228 "high" => "High",
1229 "medium" => "Medium",
1230 _ => "Low",
1231 };
1232
1233 safety_risks.push(serde_json::json!({
1234 "location": violation.get("location").and_then(|l| l.as_str()).unwrap_or("Unknown"),
1235 "operation": format!("Safety Violation: {violation_type}"),
1236 "risk_level": risk_level,
1237 "description": violation.get("description").and_then(|d| d.as_str()).unwrap_or("Safety violation detected")
1238 }));
1239 }
1240 }
1241 }
1242 }
1243
1244 if safety_risks.is_empty() {
1246 safety_risks.push(serde_json::json!({
1247 "location": "Global Analysis",
1248 "operation": "Safety Scan Complete",
1249 "risk_level": "Low",
1250 "description": "No significant safety risks detected in current allocations"
1251 }));
1252 }
1253
1254 serde_json::to_string(&safety_risks)
1255 .map_err(|e| format!("Failed to serialize safety risk data: {e}").into())
1256}
1257
1258fn inject_safety_risk_data_into_html(
1260 mut html: String,
1261 safety_risk_data: &str,
1262) -> Result<String, Box<dyn Error>> {
1263 html = html.replace(
1265 "window.safetyRisks = [];",
1266 &format!("window.safetyRisks = {safety_risk_data};"),
1267 );
1268
1269 if !html.contains("function loadSafetyRisks") {
1271 if let Some(script_end) = html.rfind("</script>") {
1273 let before = &html[..script_end];
1274 let after = &html[script_end..];
1275
1276 let safety_function_injection = r#"
1277 // Safety Risk Data Management Function
1278 function loadSafetyRisks() {
1279 console.log('🛡️ Loading safety risk data...');
1280 const unsafeTable = document.getElementById('unsafeTable');
1281 if (!unsafeTable) {
1282 console.warn('⚠️ unsafeTable not found');
1283 return;
1284 }
1285
1286 const risks = window.safetyRisks || [];
1287 if (risks.length === 0) {
1288 unsafeTable.innerHTML = '<tr><td colspan="3" class="text-center text-gray-500">No safety risks detected</td></tr>';
1289 return;
1290 }
1291
1292 unsafeTable.innerHTML = '';
1293 risks.forEach((risk, index) => {
1294 const row = document.createElement('tr');
1295 row.className = 'hover:bg-gray-50 dark:hover:bg-gray-700';
1296
1297 const riskLevelClass = risk.risk_level === 'High' ? 'text-red-600 font-bold' :
1298 risk.risk_level === 'Medium' ? 'text-yellow-600 font-semibold' :
1299 'text-green-600';
1300
1301 row.innerHTML = `
1302 <td class="px-3 py-2 text-sm">${risk.location || 'Unknown'}</td>
1303 <td class="px-3 py-2 text-sm">${risk.operation || 'Unknown'}</td>
1304 <td class="px-3 py-2 text-sm"><span class="${riskLevelClass}">${risk.risk_level || 'Low'}</span></td>
1305 `;
1306 unsafeTable.appendChild(row);
1307 });
1308
1309 console.log('✅ Safety risks loaded:', risks.length, 'items');
1310 }
1311
1312 "#;
1313
1314 html = format!("{before}{safety_function_injection}{after}");
1315 }
1316 }
1317
1318 html = html.replace("console.log('✅ Enhanced dashboard initialized');",
1320 "console.log('✅ Enhanced dashboard initialized'); setTimeout(() => { if (typeof loadSafetyRisks === 'function') { loadSafetyRisks(); } }, 100);");
1321
1322 if html.contains("manualBtn.addEventListener('click', manualInitialize);") {
1324 html = html.replace("manualBtn.addEventListener('click', manualInitialize);",
1325 "manualBtn.addEventListener('click', function() { manualInitialize(); setTimeout(() => { if (typeof loadSafetyRisks === 'function') { loadSafetyRisks(); } }, 100); });");
1326 }
1327
1328 html = html.replace(
1330 "loadSafetyRisks();",
1331 "if (typeof loadSafetyRisks === 'function') { loadSafetyRisks(); }",
1332 );
1333
1334 tracing::info!("📊 Safety risk data and function injected into HTML template");
1335
1336 Ok(html)
1337}
1338
1339#[cfg(test)]
1340mod tests {
1341 use super::*;
1342 use std::collections::HashMap;
1343
1344 #[test]
1345 fn test_generate_direct_html_with_empty_data() {
1346 let json_data = HashMap::new();
1347 let result = generate_direct_html(&json_data);
1348 assert!(result.is_err());
1349 assert_eq!(
1350 result.unwrap_err().to_string(),
1351 "No JSON data provided for HTML generation"
1352 );
1353 }
1354
1355 #[test]
1356 fn test_transform_json_data_structure_with_empty_input() {
1357 let json_data = HashMap::new();
1358 let result = transform_json_data_structure(&json_data);
1359 assert!(result.is_ok());
1360
1361 let transformed = result.unwrap();
1362 assert!(transformed.contains_key("memory_analysis"));
1363 assert!(transformed.contains_key("lifetime"));
1364 assert!(transformed.contains_key("complex_types"));
1365 assert!(transformed.contains_key("performance"));
1366 assert!(transformed.contains_key("unsafe_ffi"));
1367 assert!(transformed.contains_key("security_violations"));
1368 }
1369
1370 #[test]
1371 fn test_transform_json_data_structure_with_memory_analysis() {
1372 let mut json_data = HashMap::new();
1373 json_data.insert(
1374 "test_memory_analysis".to_string(),
1375 serde_json::json!({
1376 "allocations": [],
1377 "stats": {
1378 "total_allocations": 0,
1379 "active_allocations": 0,
1380 "total_memory": 0,
1381 "active_memory": 0
1382 }
1383 }),
1384 );
1385
1386 let result = transform_json_data_structure(&json_data);
1387 assert!(result.is_ok());
1388
1389 let transformed = result.unwrap();
1390 assert!(transformed.contains_key("memory_analysis"));
1391 }
1392
1393 #[test]
1394 fn test_transform_json_data_structure_with_lifetime_data() {
1395 let mut json_data = HashMap::new();
1396 json_data.insert(
1397 "test_lifetime".to_string(),
1398 serde_json::json!({
1399 "lifecycle_events": []
1400 }),
1401 );
1402
1403 let result = transform_json_data_structure(&json_data);
1404 assert!(result.is_ok());
1405
1406 let transformed = result.unwrap();
1407 assert!(transformed.contains_key("lifetime"));
1408 }
1409
1410 #[test]
1411 fn test_transform_json_data_structure_with_complex_types() {
1412 let mut json_data = HashMap::new();
1413 json_data.insert(
1414 "test_complex_types".to_string(),
1415 serde_json::json!({
1416 "categorized_types": {
1417 "generic_types": [],
1418 "collections": [],
1419 "smart_pointers": [],
1420 "trait_objects": []
1421 },
1422 "summary": {
1423 "total_complex_types": 0,
1424 "generic_type_count": 0
1425 }
1426 }),
1427 );
1428
1429 let result = transform_json_data_structure(&json_data);
1430 assert!(result.is_ok());
1431
1432 let transformed = result.unwrap();
1433 assert!(transformed.contains_key("complex_types"));
1434 }
1435
1436 #[test]
1437 fn test_transform_json_data_structure_with_performance_data() {
1438 let mut json_data = HashMap::new();
1439 json_data.insert(
1440 "test_performance".to_string(),
1441 serde_json::json!({
1442 "memory_performance": {
1443 "active_memory": 0,
1444 "peak_memory": 0,
1445 "total_allocated": 0
1446 },
1447 "allocation_distribution": {
1448 "tiny": 0,
1449 "small": 0,
1450 "medium": 0,
1451 "large": 0,
1452 "massive": 0
1453 }
1454 }),
1455 );
1456
1457 let result = transform_json_data_structure(&json_data);
1458 assert!(result.is_ok());
1459
1460 let transformed = result.unwrap();
1461 assert!(transformed.contains_key("performance"));
1462 }
1463
1464 #[test]
1465 fn test_transform_json_data_structure_with_ffi_data() {
1466 let mut json_data = HashMap::new();
1467 json_data.insert(
1468 "test_unsafe_ffi".to_string(),
1469 serde_json::json!({
1470 "summary": {
1471 "total_risk_items": 0,
1472 "unsafe_count": 0,
1473 "ffi_count": 0,
1474 "safety_violations": 0
1475 },
1476 "enhanced_ffi_data": [],
1477 "safety_violations": []
1478 }),
1479 );
1480
1481 let result = transform_json_data_structure(&json_data);
1482 assert!(result.is_ok());
1483
1484 let transformed = result.unwrap();
1485 assert!(transformed.contains_key("unsafe_ffi"));
1486 }
1487
1488 #[test]
1489 fn test_transform_json_data_structure_with_security_violations() {
1490 let mut json_data = HashMap::new();
1491 json_data.insert(
1492 "test_security_violations".to_string(),
1493 serde_json::json!({
1494 "metadata": {
1495 "total_violations": 0
1496 },
1497 "violation_reports": [],
1498 "security_summary": {
1499 "security_analysis_summary": {
1500 "total_violations": 0,
1501 "severity_breakdown": {
1502 "critical": 0,
1503 "high": 0,
1504 "medium": 0,
1505 "low": 0,
1506 "info": 0
1507 }
1508 }
1509 }
1510 }),
1511 );
1512
1513 let result = transform_json_data_structure(&json_data);
1514 assert!(result.is_ok());
1515
1516 let transformed = result.unwrap();
1517 assert!(transformed.contains_key("security_violations"));
1518 }
1519
1520 #[test]
1521 fn test_enhance_memory_analysis_data() {
1522 let data = serde_json::json!({
1523 "allocations": []
1524 });
1525
1526 let result = enhance_memory_analysis_data(&data);
1527 assert!(result.is_ok());
1528 }
1529
1530 #[test]
1531 fn test_enhance_lifetime_data() {
1532 let data = serde_json::json!({
1533 "lifecycle_events": []
1534 });
1535
1536 let result = enhance_lifetime_data(&data);
1537 assert!(result.is_ok());
1538 }
1539
1540 #[test]
1541 fn test_enhance_ffi_data() {
1542 let data = serde_json::json!({
1543 "allocations": [],
1544 "boundary_events": []
1545 });
1546
1547 let result = enhance_ffi_data(&data);
1548 assert!(result.is_ok());
1549 }
1550
1551 #[test]
1552 fn test_analyze_memory_fragmentation() {
1553 let allocations = vec![];
1554 let result = analyze_memory_fragmentation(&allocations);
1555 assert_eq!(result["total_blocks"], serde_json::json!(0));
1556 }
1557
1558 #[test]
1559 fn test_analyze_memory_growth_trends() {
1560 let allocations = vec![];
1561 let result = analyze_memory_growth_trends(&allocations);
1562 assert_eq!(result["peak_memory"], serde_json::json!(0));
1563 }
1564
1565 #[test]
1566 fn test_calculate_ffi_statistics_from_allocations() {
1567 let allocations = vec![];
1568 let boundary_events = vec![];
1569 let result = calculate_ffi_statistics_from_allocations(&allocations, &boundary_events);
1570 assert_eq!(result["total_allocations"], serde_json::json!(0));
1571 }
1572
1573 #[test]
1574 fn test_analyze_language_interactions() {
1575 let boundary_events = vec![];
1576 let result = analyze_language_interactions(&boundary_events);
1577 assert_eq!(result, serde_json::json!([]));
1578 }
1579
1580 #[test]
1581 fn test_analyze_safety_metrics_from_allocations() {
1582 let allocations = vec![];
1583 let result = analyze_safety_metrics_from_allocations(&allocations);
1584 assert_eq!(result["total_operations"], serde_json::json!(0));
1585 }
1586
1587 #[test]
1588 fn test_get_progress_color() {
1589 let color = get_progress_color(0);
1590 assert_eq!(color, "#ff6b6b");
1591
1592 let color = get_progress_color(10);
1593 assert_eq!(color, "#ff6b6b"); }
1595
1596 #[test]
1597 fn test_get_fragmentation_analysis() {
1598 let analysis = get_fragmentation_analysis(5);
1599 assert_eq!(
1600 analysis,
1601 "Excellent memory layout with minimal fragmentation."
1602 );
1603
1604 let analysis = get_fragmentation_analysis(15);
1605 assert_eq!(analysis, "Good memory layout with low fragmentation.");
1606
1607 let analysis = get_fragmentation_analysis(35);
1608 assert_eq!(
1609 analysis,
1610 "Moderate fragmentation detected. Consider memory pool allocation."
1611 );
1612
1613 let analysis = get_fragmentation_analysis(60);
1614 assert_eq!(
1615 analysis,
1616 "High fragmentation detected. Memory layout optimization recommended."
1617 );
1618 }
1619
1620 #[test]
1621 fn test_get_trend_analysis() {
1622 let analysis = get_trend_analysis(-5);
1623 assert_eq!(
1624 analysis,
1625 "Memory usage is decreasing - good memory management."
1626 );
1627
1628 let analysis = get_trend_analysis(5);
1629 assert_eq!(analysis, "Stable memory usage with minimal growth.");
1630
1631 let analysis = get_trend_analysis(25);
1632 assert_eq!(
1633 analysis,
1634 "Moderate memory growth - monitor for potential leaks."
1635 );
1636
1637 let analysis = get_trend_analysis(75);
1638 assert_eq!(
1639 analysis,
1640 "High memory growth detected - investigate for memory leaks."
1641 );
1642 }
1643
1644 #[test]
1645 fn test_format_memory_size() {
1646 let formatted = format_memory_size(1023);
1647 assert_eq!(formatted, "1023 B");
1648
1649 let formatted = format_memory_size(1024);
1650 assert_eq!(formatted, "1.0 KB");
1651
1652 let formatted = format_memory_size(1024 * 1024);
1653 assert_eq!(formatted, "1.0 MB");
1654
1655 let formatted = format_memory_size(1024 * 1024 * 1024);
1656 assert_eq!(formatted, "1.0 GB");
1657 }
1658
1659 #[test]
1660 fn test_calculate_risk_level() {
1661 let risk = calculate_risk_level(100, true, false);
1662 assert_eq!(risk, "HIGH");
1663
1664 let risk = calculate_risk_level(1024 * 1024 + 1, false, true);
1665 assert_eq!(risk, "MEDIUM");
1666
1667 let risk = calculate_risk_level(100, false, true);
1668 assert_eq!(risk, "LOW");
1669
1670 let risk = calculate_risk_level(100, false, false);
1671 assert_eq!(risk, "SAFE");
1672 }
1673
1674 #[test]
1675 fn test_create_ffi_dashboard_metrics() {
1676 let allocations = vec![];
1677 let boundary_events = vec![];
1678 let result = create_ffi_dashboard_metrics(&allocations, &boundary_events);
1679 assert_eq!(result["total_allocations"], serde_json::json!(0));
1680 }
1681
1682 #[test]
1683 fn test_analyze_smart_pointer_types() {
1684 let allocations = vec![];
1685 let result = analyze_smart_pointer_types(&allocations);
1686 assert_eq!(result, serde_json::json!({}));
1687 }
1688
1689 #[test]
1690 fn test_analyze_borrow_checker_metrics() {
1691 let allocations = vec![];
1692 let result = analyze_borrow_checker_metrics(&allocations);
1693 assert_eq!(result["max_concurrent_borrows"], serde_json::json!(0));
1694 }
1695
1696 #[test]
1697 fn test_analyze_memory_hotspots() {
1698 let allocations = vec![];
1699 let result = analyze_memory_hotspots(&allocations);
1700 assert_eq!(result, serde_json::json!([]));
1701 }
1702
1703 #[test]
1704 fn test_analyze_cross_language_memory_flow() {
1705 let allocations = vec![];
1706 let boundary_events = vec![];
1707 let result = analyze_cross_language_memory_flow(&allocations, &boundary_events);
1708 assert_eq!(result["rust_allocations"], serde_json::json!(0));
1709 }
1710
1711 #[test]
1712 fn test_create_ffi_risk_assessment() {
1713 let allocations = vec![];
1714 let result = create_ffi_risk_assessment(&allocations);
1715 assert_eq!(result["summary"]["total_risks"], serde_json::json!(0));
1716 }
1717
1718 #[test]
1719 fn test_get_violation_severity() {
1720 let severity = get_violation_severity("double free detected");
1721 assert_eq!(severity, "critical");
1722
1723 let severity = get_violation_severity("invalid free operation");
1724 assert_eq!(severity, "high");
1725
1726 let severity = get_violation_severity("memory leak detected");
1727 assert_eq!(severity, "medium");
1728
1729 let severity = get_violation_severity("unknown issue");
1730 assert_eq!(severity, "low");
1731 }
1732
1733 #[test]
1734 fn test_generate_safety_risk_data_from_json() {
1735 let transformed_data = serde_json::Map::new();
1736 let result = generate_safety_risk_data_from_json(&transformed_data);
1737 assert!(result.is_ok());
1738 }
1739
1740 #[test]
1741 fn test_inject_safety_risk_data_into_html() {
1742 let html = "<script>window.safetyRisks = [];</script>".to_string();
1743 let safety_risk_data = "[]";
1744 let result = inject_safety_risk_data_into_html(html, safety_risk_data);
1745 assert!(result.is_ok());
1746 }
1747}