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