memscope_rs/render_engine/dashboard/renderer/
mod.rs1mod context;
7mod event_dto;
8mod event_reconstructor;
9mod helpers;
10mod inference;
11mod render_methods;
12mod report_builder;
13mod system_info;
14mod types;
15
16pub use types::*;
17
18pub use event_dto::{build_data_index, DashboardEventDTO, DataIndex, EventSummary};
20pub use event_reconstructor::rebuild_allocations_from_events;
21
22use crate::analysis::memory_passport_tracker::MemoryPassportTracker;
23use crate::tracker::Tracker;
24use handlebars::Handlebars;
25use std::sync::Arc;
26
27pub struct DashboardRenderer {
29 handlebars: Handlebars<'static>,
30}
31
32impl DashboardRenderer {
33 pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
35 let mut handlebars = Handlebars::new();
36
37 let template_path = format!(
38 "{}/src/render_engine/dashboard/templates/dashboard_unified.html",
39 env!("CARGO_MANIFEST_DIR")
40 );
41 handlebars.register_template_file("dashboard_unified", &template_path)?;
42
43 let final_path = format!(
44 "{}/src/render_engine/dashboard/templates/dashboard_final.html",
45 env!("CARGO_MANIFEST_DIR")
46 );
47 handlebars.register_template_file("dashboard_final", &final_path)?;
48
49 helpers::register_helpers(&mut handlebars);
50
51 Ok(Self { handlebars })
52 }
53
54 pub fn build_context_from_tracker(
56 &self,
57 tracker: &Tracker,
58 passport_tracker: &Arc<MemoryPassportTracker>,
59 ) -> Result<DashboardContext, Box<dyn std::error::Error>> {
60 self.build_context_from_tracker_with_async(tracker, passport_tracker, None)
61 }
62
63 pub fn build_context_from_tracker_with_async(
65 &self,
66 tracker: &Tracker,
67 passport_tracker: &Arc<MemoryPassportTracker>,
68 async_tracker: Option<&Arc<crate::capture::backends::async_tracker::AsyncTracker>>,
69 ) -> Result<DashboardContext, Box<dyn std::error::Error>> {
70 context::build_context_from_tracker_with_async(tracker, passport_tracker, async_tracker)
71 }
72
73 pub fn render_from_tracker(
75 &self,
76 tracker: &Tracker,
77 passport_tracker: &Arc<MemoryPassportTracker>,
78 ) -> Result<String, Box<dyn std::error::Error>> {
79 let context = self.build_context_from_tracker(tracker, passport_tracker)?;
80 self.render_dashboard(&context)
81 }
82
83 pub fn render_dashboard(
85 &self,
86 context: &DashboardContext,
87 ) -> Result<String, Box<dyn std::error::Error>> {
88 self.render_unified_dashboard(context)
89 }
90
91 pub fn render_standalone_dashboard(
93 &self,
94 context: &DashboardContext,
95 ) -> Result<String, Box<dyn std::error::Error>> {
96 self.render_unified_dashboard(context)
97 }
98
99 pub fn render_unified_dashboard(
101 &self,
102 context: &DashboardContext,
103 ) -> Result<String, Box<dyn std::error::Error>> {
104 render_methods::render_unified_dashboard(&self.handlebars, context)
105 }
106
107 pub fn render_final_dashboard(
109 &self,
110 context: &DashboardContext,
111 ) -> Result<String, Box<dyn std::error::Error>> {
112 render_methods::render_final_dashboard(&self.handlebars, context)
113 }
114
115 pub fn render_binary_dashboard(
117 &self,
118 context: &DashboardContext,
119 ) -> Result<String, Box<dyn std::error::Error>> {
120 render_methods::render_binary_dashboard(&self.handlebars, context)
121 }
122
123 pub fn render_clean_dashboard(
125 &self,
126 context: &DashboardContext,
127 ) -> Result<String, Box<dyn std::error::Error>> {
128 render_methods::render_clean_dashboard(&self.handlebars, context)
129 }
130
131 pub fn render_hybrid_dashboard(
133 &self,
134 context: &DashboardContext,
135 ) -> Result<String, Box<dyn std::error::Error>> {
136 render_methods::render_hybrid_dashboard(&self.handlebars, context)
137 }
138
139 pub fn render_performance_dashboard(
141 &self,
142 context: &DashboardContext,
143 ) -> Result<String, Box<dyn std::error::Error>> {
144 render_methods::render_performance_dashboard(&self.handlebars, context)
145 }
146}
147
148impl Default for DashboardRenderer {
149 fn default() -> Self {
150 Self::new().expect("Failed to create dashboard renderer")
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157 use crate::render_engine::dashboard::renderer::types::{
158 AsyncSummary, CircularReferenceReport, DashboardContext, OwnershipGraphInfo,
159 SystemResources,
160 };
161
162 fn create_empty_context() -> DashboardContext {
163 DashboardContext {
164 title: "Test".to_string(),
165 export_timestamp: "2024-01-01".to_string(),
166 total_memory: "0 B".to_string(),
167 total_allocations: 0,
168 active_allocations: 0,
169 peak_memory: "0 B".to_string(),
170 thread_count: 0,
171 passport_count: 0,
172 leak_count: 0,
173 unsafe_count: 0,
174 ffi_count: 0,
175 allocations: vec![],
176 relationships: vec![],
177 unsafe_reports: vec![],
178 passport_details: vec![],
179 allocations_count: 0,
180 relationships_count: 0,
181 unsafe_reports_count: 0,
182 json_data: "{}".to_string(),
183 os_name: "test".to_string(),
184 architecture: "test".to_string(),
185 cpu_cores: 1,
186 system_resources: SystemResources {
187 os_name: "test".to_string(),
188 os_version: "1.0".to_string(),
189 architecture: "test".to_string(),
190 cpu_cores: 1,
191 total_physical: "0 B".to_string(),
192 available_physical: "0 B".to_string(),
193 used_physical: "0 B".to_string(),
194 page_size: 4096,
195 },
196 threads: vec![],
197 async_tasks: vec![],
198 async_summary: AsyncSummary {
199 total_tasks: 0,
200 active_tasks: 0,
201 total_allocations: 0,
202 total_memory_bytes: 0,
203 peak_memory_bytes: 0,
204 },
205 health_score: 100,
206 health_status: "Good".to_string(),
207 safe_ops_count: 0,
208 high_risk_count: 0,
209 clean_passport_count: 0,
210 active_passport_count: 0,
211 leaked_passport_count: 0,
212 ffi_tracked_count: 0,
213 safe_code_percent: 100,
214 ownership_graph: OwnershipGraphInfo {
215 total_nodes: 0,
216 total_edges: 0,
217 total_cycles: 0,
218 rc_clone_count: 0,
219 arc_clone_count: 0,
220 has_issues: false,
221 issues: vec![],
222 root_cause: None,
223 },
224 top_allocation_sites: vec![],
225 top_leaked_allocations: vec![],
226 top_temporary_churn: vec![],
227 circular_references: CircularReferenceReport {
228 count: 0,
229 total_leaked_memory: 0,
230 pointers_in_cycles: 0,
231 total_smart_pointers: 0,
232 has_cycles: false,
233 },
234 task_graph_json: "{}".to_string(),
235 }
236 }
237
238 #[test]
241 fn test_dashboard_renderer_creation() {
242 let result = DashboardRenderer::new();
243 assert!(
244 result.is_ok(),
245 "DashboardRenderer should create successfully"
246 );
247 }
248
249 #[test]
252 fn test_dashboard_renderer_default() {
253 let renderer = DashboardRenderer::default();
254 let _ = &renderer;
255 }
256
257 #[test]
260 fn test_render_unified_dashboard() {
261 let renderer = DashboardRenderer::new().expect("Should create renderer");
262 let context = create_empty_context();
263 let result = renderer.render_unified_dashboard(&context);
264 assert!(
265 result.is_ok(),
266 "Should render unified dashboard successfully"
267 );
268 }
269
270 #[test]
273 fn test_render_final_dashboard() {
274 let renderer = DashboardRenderer::new().expect("Should create renderer");
275 let context = create_empty_context();
276 let result = renderer.render_final_dashboard(&context);
277 assert!(result.is_ok(), "Should render final dashboard successfully");
278 }
279
280 #[test]
283 fn test_render_dashboard() {
284 let renderer = DashboardRenderer::new().expect("Should create renderer");
285 let context = create_empty_context();
286 let result = renderer.render_dashboard(&context);
287 assert!(result.is_ok(), "Should render dashboard successfully");
288 }
289
290 #[test]
293 fn test_render_standalone_dashboard() {
294 let renderer = DashboardRenderer::new().expect("Should create renderer");
295 let context = create_empty_context();
296 let result = renderer.render_standalone_dashboard(&context);
297 assert!(
298 result.is_ok(),
299 "Should render standalone dashboard successfully"
300 );
301 }
302}