memscope_rs/cli/commands/html_from_json/
direct_json_template.rs

1//! Direct JSON template generator that uses raw JSON data without complex processing
2
3use serde_json::Value;
4use std::collections::HashMap;
5use std::error::Error;
6
7/// Generate HTML directly from raw JSON data
8pub 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    // Validate that we have essential data
12    if json_data.is_empty() {
13        return Err("No JSON data provided for HTML generation".into());
14    }
15
16    // Log what data we have
17    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    // Transform the data structure to match JavaScript expectations
26    let transformed_data = transform_json_data_structure(json_data)?;
27
28    // Generate safety risk data from the transformed allocations
29    let safety_risk_data = generate_safety_risk_data_from_json(&transformed_data)?;
30
31    // Serialize the transformed JSON data for embedding with proper escaping
32    let json_data_str = serde_json::to_string(&transformed_data)
33        .map_err(|e| format!("Failed to serialize JSON data: {e}"))?;
34
35    // Debug: Log data serialization info
36    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    // Log data structure for debugging
52    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    // Try multiple possible paths for the template files - prioritize the original dashboard.html
59    let template_paths = [
60        // Primary: Use the new clean dashboard template
61        "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    // Load CSS content
87    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    // Load JavaScript content
93    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    // Replace placeholders in the template with proper escaping
99    let mut html = template_content
100        .replace("{{ json_data }}", &json_data_str) // with spaces
101        .replace("{{json_data}}", &json_data_str) // without spaces
102        .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        ) // fix CSS format issues
109        .replace(
110            "{\n                {\n                CSS_CONTENT\n            }\n        }",
111            &css_content,
112        ); // alternative format
113
114    // Inject safety risk data into the HTML
115    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
125/// Transform the raw JSON data structure to match JavaScript expectations
126/// This function preprocesses data in Rust to create visualization-ready structures
127fn 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    // Process each JSON file and map it to the expected structure
133    for (file_key, file_data) in json_data {
134        // Extract the data type from the filename
135        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            // Also add it with the specific key that JavaScript expects
149            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            // Keep any other data with its original key
156            transformed.insert(file_key.clone(), file_data.clone());
157        }
158    }
159
160    // Ensure we have all expected data structures, even if empty
161    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
271/// Enhance memory analysis data with visualization-ready structures
272fn 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        // Add memory fragmentation analysis
277        let fragmentation_data = analyze_memory_fragmentation(allocations);
278
279        // Add memory growth trends
280        let growth_trends = analyze_memory_growth_trends(allocations);
281
282        // Create enhanced structure
283        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
293/// Enhance lifetime data with colorful progress bar information
294fn 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        // Filter for user-defined variables
299        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        // Group by variable name and add color information
314        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; // 10 colors in palette
318                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        // Convert to array and add to enhanced data
339        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
357/// Enhance FFI data with comprehensive analysis and SVG-inspired visualization data
358fn enhance_ffi_data(data: &Value) -> Result<Value, Box<dyn Error>> {
359    let mut enhanced = data.clone();
360
361    let empty_vec = vec![];
362    // Use the actual allocations field from the JSON data
363    let allocations = data
364        .get("allocations")
365        .and_then(|d| d.as_array())
366        .unwrap_or(&empty_vec);
367
368    // Fallback to enhanced_ffi_data if allocations is not found
369    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    // Calculate comprehensive statistics using the actual allocations
390    let stats = calculate_ffi_statistics_from_allocations(enhanced_data, boundary_events);
391
392    // Analyze language interactions
393    let language_interactions = analyze_language_interactions(boundary_events);
394
395    // Safety analysis using actual allocations
396    let safety_analysis = analyze_safety_metrics_from_allocations(enhanced_data);
397
398    // Create SVG-inspired dashboard metrics
399    let dashboard_metrics = create_ffi_dashboard_metrics(enhanced_data, boundary_events);
400
401    // Create memory hotspots analysis
402    let memory_hotspots = analyze_memory_hotspots(enhanced_data);
403
404    // Create cross-language memory flow analysis
405    let memory_flow = analyze_cross_language_memory_flow(enhanced_data, boundary_events);
406
407    // Create risk assessment
408    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        // Ensure allocations are preserved in the enhanced data
420        if !allocations.is_empty() {
421            obj.insert("allocations".to_string(), serde_json::json!(allocations));
422        }
423    }
424
425    Ok(enhanced)
426}
427
428/// Analyze memory fragmentation from allocations
429fn 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
479/// Analyze memory growth trends
480fn 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) // Limit for performance
505        .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 // Convert to seconds
541        } 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
564/// Calculate comprehensive FFI statistics from allocations
565fn 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    // Count safety violations from arrays
583    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    // Count borrow conflicts
594    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    // Count clones
614    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
641/// Analyze language interactions from boundary events
642fn 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
668/// Analyze safety metrics from allocations
669fn analyze_safety_metrics_from_allocations(allocations: &[Value]) -> Value {
670    let safe_operations = allocations
671        .iter()
672        .filter(|item| {
673            // Check if safety_violations array is empty
674            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    // Count allocations with ownership history
691    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    // Count leaked allocations
701    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
720/// Analyze safety metrics (legacy function for backward compatibility)
721fn _analyze_safety_metrics(enhanced_data: &[Value]) -> Value {
722    analyze_safety_metrics_from_allocations(enhanced_data)
723}
724
725/// Get progress bar color by index
726fn 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
734/// Get fragmentation analysis text
735fn 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
744/// Get trend analysis text
745fn 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
754/// Format memory size for display
755fn 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
772/// Calculate risk level for memory allocation
773fn 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
785/// Create FFI dashboard metrics inspired by SVG design
786fn create_ffi_dashboard_metrics(allocations: &[Value], boundary_events: &[Value]) -> Value {
787    let total_allocations = allocations.len();
788
789    // Count unsafe allocations (those with safety violations)
790    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    // Count FFI-tracked allocations
801    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    // Count boundary crossings
811    let boundary_crossings = boundary_events.len();
812
813    // Count safety violations
814    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    // Calculate total unsafe memory
825    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    // Calculate safety score
837    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    // Analyze smart pointer types
844    let smart_pointer_types = analyze_smart_pointer_types(allocations);
845
846    // Analyze borrow checker metrics
847    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
863/// Analyze smart pointer types distribution
864fn 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                // Extract the main type name
875                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
895/// Analyze borrow checker metrics
896fn 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            // Check for conflicts (both immutable and mutable borrows)
922            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
935/// Analyze memory hotspots for visualization
936fn 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    // Sort by size descending
970    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
979/// Analyze cross-language memory flow
980fn 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    // Analyze flow directions from boundary events
994    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
1019/// Create FFI risk assessment
1020fn 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        // Check for potential risks based on borrow patterns
1045        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    // Calculate risk summary
1069    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
1098/// Get violation severity
1099fn 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
1108/// Generate safety risk data from JSON data structure
1109fn 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    // Extract allocations from memory_analysis
1115    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                // Check for potential unsafe operations based on allocation patterns
1122
1123                // 1. Large allocations that might indicate unsafe buffer operations
1124                if let Some(size) = allocation.get("size").and_then(|s| s.as_u64()) {
1125                    if size > 1024 * 1024 {
1126                        // > 1MB
1127                        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                // 2. Leaked memory indicates potential unsafe operations
1139                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                // 3. High borrow count might indicate unsafe sharing
1153                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                // 4. Raw pointer types indicate direct unsafe operations
1168                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                    // 5. FFI-related types
1181                    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                // 6. Very short-lived allocations might indicate unsafe temporary operations
1198                if let Some(lifetime_ms) = allocation.get("lifetime_ms").and_then(|l| l.as_u64()) {
1199                    if lifetime_ms < 1 {
1200                        // Less than 1ms
1201                        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    // Check unsafe_ffi data for additional risks
1216    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 no risks found, add a placeholder to show the system is working
1245    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
1258/// Inject safety risk data into HTML template
1259fn inject_safety_risk_data_into_html(
1260    mut html: String,
1261    safety_risk_data: &str,
1262) -> Result<String, Box<dyn Error>> {
1263    // Replace the safety risk data in the existing template
1264    html = html.replace(
1265        "window.safetyRisks = [];",
1266        &format!("window.safetyRisks = {safety_risk_data};"),
1267    );
1268
1269    // Always ensure loadSafetyRisks function is available
1270    if !html.contains("function loadSafetyRisks") {
1271        // Find a good injection point - before the closing </script> tag
1272        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    // Ensure safety risks are loaded after initialization - but only call if function exists
1319    html = html.replace("console.log('✅ Enhanced dashboard initialized');", 
1320                       "console.log('✅ Enhanced dashboard initialized'); setTimeout(() => { if (typeof loadSafetyRisks === 'function') { loadSafetyRisks(); } }, 100);");
1321
1322    // Also add to manual initialization if it exists - with safer replacement
1323    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    // Remove any standalone loadSafetyRisks calls that might cause errors
1329    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"); // Should wrap around
1594    }
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}