Skip to main content

memscope_rs/render_engine/
engine.rs

1//! Render Engine - Output rendering
2//!
3//! This module provides the RenderEngine which coordinates rendering
4/// of memory data in various formats.
5use crate::render_engine::renderer::{OutputFormat, RenderConfig, RenderResult, Renderer};
6use crate::snapshot::{MemorySnapshot, SharedSnapshotEngine};
7use serde_json;
8
9/// JSON Renderer
10struct JsonRenderer;
11
12impl Renderer for JsonRenderer {
13    fn format(&self) -> OutputFormat {
14        OutputFormat::Json
15    }
16
17    fn render(
18        &self,
19        snapshot: &MemorySnapshot,
20        config: &RenderConfig,
21    ) -> Result<RenderResult, String> {
22        let data = if config.verbose {
23            serde_json::to_vec_pretty(&snapshot)
24        } else {
25            serde_json::to_vec(&snapshot)
26        }
27        .map_err(|e| e.to_string())?;
28
29        let size = data.len();
30
31        Ok(RenderResult {
32            data,
33            format: OutputFormat::Json,
34            size,
35        })
36    }
37}
38
39/// Render Engine - Coordinates output rendering
40///
41/// The RenderEngine manages multiple renderers and provides a unified
42/// interface for rendering memory data in various formats.
43///
44/// Key properties:
45/// - Pluggable: Supports adding custom renderers
46/// - Flexible: Supports multiple output formats
47/// - Configurable: Supports various rendering options
48pub struct RenderEngine {
49    /// Reference to the snapshot engine
50    snapshot_engine: SharedSnapshotEngine,
51    /// Registered renderers
52    renderers: Vec<Box<dyn Renderer>>,
53}
54
55impl RenderEngine {
56    /// Create a new RenderEngine
57    pub fn new(snapshot_engine: SharedSnapshotEngine) -> Self {
58        let mut engine = Self {
59            snapshot_engine,
60            renderers: Vec::new(),
61        };
62
63        // Register default renderers
64        engine.register_renderer(Box::new(JsonRenderer));
65
66        engine
67    }
68
69    /// Register a renderer
70    ///
71    /// # Arguments
72    /// * `renderer` - The renderer to register
73    pub fn register_renderer(&mut self, renderer: Box<dyn Renderer>) {
74        self.renderers.push(renderer);
75    }
76
77    /// Render the current snapshot with the specified configuration
78    ///
79    /// # Arguments
80    /// * `config` - Render configuration
81    ///
82    /// # Returns
83    /// Result containing the rendered data or an error
84    pub fn render(&self, config: &RenderConfig) -> Result<RenderResult, String> {
85        let snapshot = self.snapshot_engine.build_snapshot();
86        self.render_snapshot(&snapshot, config)
87    }
88
89    /// Render a specific snapshot with the specified configuration
90    ///
91    /// # Arguments
92    /// * `snapshot` - The snapshot to render
93    /// * `config` - Render configuration
94    ///
95    /// # Returns
96    /// Result containing the rendered data or an error
97    pub fn render_snapshot(
98        &self,
99        snapshot: &MemorySnapshot,
100        config: &RenderConfig,
101    ) -> Result<RenderResult, String> {
102        // Find a renderer for the requested format
103        for renderer in &self.renderers {
104            if renderer.format() == config.format {
105                return renderer.render(snapshot, config);
106            }
107        }
108
109        Err(format!("No renderer found for format: {}", config.format))
110    }
111
112    /// Render to JSON format
113    pub fn render_json(
114        &self,
115        snapshot: &MemorySnapshot,
116        verbose: bool,
117    ) -> Result<RenderResult, String> {
118        let config = RenderConfig {
119            format: OutputFormat::Json,
120            output_path: None,
121            verbose,
122            include_timestamps: true,
123        };
124        self.render_snapshot(snapshot, &config)
125    }
126
127    /// Check if a renderer is available for the specified format
128    ///
129    /// # Arguments
130    /// * `format` - The output format to check
131    pub fn has_renderer(&self, format: OutputFormat) -> bool {
132        self.renderers.iter().any(|r| r.format() == format)
133    }
134
135    /// Get the number of registered renderers
136    pub fn renderer_count(&self) -> usize {
137        self.renderers.len()
138    }
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144    use crate::event_store::EventStore;
145    use crate::snapshot::SnapshotEngine;
146    use std::sync::Arc;
147
148    #[test]
149    fn test_render_engine_creation() {
150        let event_store = Arc::new(EventStore::new());
151        let snapshot_engine = Arc::new(SnapshotEngine::new(event_store));
152        let engine = RenderEngine::new(snapshot_engine);
153
154        assert!(engine.has_renderer(OutputFormat::Json));
155        assert_eq!(engine.renderer_count(), 1);
156    }
157
158    #[test]
159    fn test_render_json() {
160        let event_store = Arc::new(EventStore::new());
161        event_store.record(crate::event_store::MemoryEvent::allocate(0x1000, 1024, 1));
162
163        let snapshot_engine = Arc::new(SnapshotEngine::new(event_store));
164        let engine = RenderEngine::new(snapshot_engine);
165
166        let snapshot = engine.snapshot_engine.build_snapshot();
167        let result = engine.render_json(&snapshot, false);
168
169        assert!(result.is_ok());
170        let result = result.unwrap();
171        assert_eq!(result.format, OutputFormat::Json);
172        assert!(result.size > 0);
173    }
174
175    #[test]
176    fn test_render_with_config() {
177        let event_store = Arc::new(EventStore::new());
178        let snapshot_engine = Arc::new(SnapshotEngine::new(event_store));
179        let engine = RenderEngine::new(snapshot_engine);
180
181        let config = RenderConfig::default();
182        let result = engine.render(&config);
183
184        assert!(result.is_ok());
185    }
186
187    #[test]
188    fn test_has_renderer() {
189        let event_store = Arc::new(EventStore::new());
190        let snapshot_engine = Arc::new(SnapshotEngine::new(event_store));
191        let engine = RenderEngine::new(snapshot_engine);
192
193        assert!(engine.has_renderer(OutputFormat::Json));
194        assert!(!engine.has_renderer(OutputFormat::Html));
195    }
196}