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