Skip to main content

trustformers_debug/
report_generation.rs

1//! Report Generation for TrustformeRS Debug
2//!
3//! This module provides comprehensive reporting capabilities for debugging,
4//! analysis, and documentation. Supports multiple output formats including
5//! PDF, Markdown, HTML, JSON, and Jupyter notebooks.
6// reason: debug/profiling scaffolding — structs are constructed and their fields/methods
7// are retained for the data model, serialization completeness, and future consumers that
8// do not yet read every member. Consolidated from many item-level #[allow(dead_code)].
9#![allow(dead_code)]
10
11use crate::{
12    gradient_debugger::GradientDebugReport,
13    profiler::ProfilerReport,
14    visualization::{DebugVisualizer, PlotData, VisualizationConfig},
15};
16use chrono::{DateTime, Utc};
17use serde::{Deserialize, Serialize};
18use std::collections::HashMap;
19
20/// Report format options
21#[derive(Debug, Clone, Serialize, Deserialize)]
22pub enum ReportFormat {
23    /// PDF format
24    Pdf,
25    /// Markdown format
26    Markdown,
27    /// HTML format
28    Html,
29    /// JSON format
30    Json,
31    /// Jupyter notebook format
32    Jupyter,
33    /// LaTeX format
34    Latex,
35    /// Excel format
36    Excel,
37    /// PowerPoint format
38    PowerPoint,
39}
40
41/// Report type categories
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub enum ReportType {
44    /// Model debugging report
45    DebugReport,
46    /// Performance analysis report
47    PerformanceReport,
48    /// Training analysis report
49    TrainingReport,
50    /// Gradient analysis report
51    GradientReport,
52    /// Memory analysis report
53    MemoryReport,
54    /// Comprehensive report (all sections)
55    ComprehensiveReport,
56    /// Custom report with specific sections
57    CustomReport(Vec<ReportSection>),
58}
59
60/// Available report sections
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub enum ReportSection {
63    /// Executive summary
64    Summary,
65    /// Model architecture analysis
66    Architecture,
67    /// Performance metrics
68    Performance,
69    /// Memory analysis
70    Memory,
71    /// Gradient analysis
72    Gradients,
73    /// Training dynamics
74    Training,
75    /// Error analysis
76    Errors,
77    /// Recommendations
78    Recommendations,
79    /// Visualizations
80    Visualizations,
81    /// Raw data
82    RawData,
83}
84
85/// Report configuration
86#[derive(Debug, Clone, Serialize, Deserialize)]
87pub struct ReportConfig {
88    /// Report title
89    pub title: String,
90    /// Report subtitle
91    pub subtitle: Option<String>,
92    /// Author information
93    pub author: String,
94    /// Organization
95    pub organization: Option<String>,
96    /// Report format
97    pub format: ReportFormat,
98    /// Report type
99    pub report_type: ReportType,
100    /// Include visualizations
101    pub include_visualizations: bool,
102    /// Include raw data
103    pub include_raw_data: bool,
104    /// Output path
105    pub output_path: String,
106    /// Additional metadata
107    pub metadata: HashMap<String, String>,
108}
109
110impl Default for ReportConfig {
111    fn default() -> Self {
112        Self {
113            title: "TrustformeRS Debug Report".to_string(),
114            subtitle: None,
115            author: "TrustformeRS Debugger".to_string(),
116            organization: None,
117            format: ReportFormat::Html,
118            report_type: ReportType::ComprehensiveReport,
119            include_visualizations: true,
120            include_raw_data: false,
121            output_path: "debug_report".to_string(),
122            metadata: HashMap::new(),
123        }
124    }
125}
126
127/// Generated report content
128#[derive(Debug, Clone, Serialize, Deserialize)]
129pub struct Report {
130    /// Report metadata
131    pub metadata: ReportMetadata,
132    /// Report sections
133    pub sections: Vec<GeneratedSection>,
134    /// Visualizations
135    pub visualizations: HashMap<String, PlotData>,
136    /// Raw data
137    pub raw_data: HashMap<String, serde_json::Value>,
138    /// Generation timestamp
139    pub generated_at: DateTime<Utc>,
140}
141
142/// Report metadata
143#[derive(Debug, Clone, Serialize, Deserialize)]
144pub struct ReportMetadata {
145    /// Report title
146    pub title: String,
147    /// Report subtitle
148    pub subtitle: Option<String>,
149    /// Author
150    pub author: String,
151    /// Organization
152    pub organization: Option<String>,
153    /// Report version
154    pub version: String,
155    /// Generation time
156    pub generation_time_ms: f64,
157    /// Additional metadata
158    pub additional_metadata: HashMap<String, String>,
159}
160
161/// Generated report section
162#[derive(Debug, Clone, Serialize, Deserialize)]
163pub struct GeneratedSection {
164    /// Section type
165    pub section_type: ReportSection,
166    /// Section title
167    pub title: String,
168    /// Section content
169    pub content: String,
170    /// Section data
171    pub data: HashMap<String, serde_json::Value>,
172    /// Associated visualizations
173    pub visualizations: Vec<String>,
174}
175
176/// Report generator
177#[derive(Debug)]
178pub struct ReportGenerator {
179    /// Configuration
180    config: ReportConfig,
181    /// Debug data
182    debug_data: Option<GradientDebugReport>,
183    /// Profiling data
184    profiling_data: Option<ProfilerReport>,
185    /// Visualizer
186    visualizer: DebugVisualizer,
187}
188
189impl ReportGenerator {
190    /// Create a new report generator
191    pub fn new(config: ReportConfig) -> Self {
192        Self {
193            config,
194            debug_data: None,
195            profiling_data: None,
196            visualizer: DebugVisualizer::new(VisualizationConfig::default()),
197        }
198    }
199
200    /// Add gradient debug data
201    pub fn with_debug_data(mut self, data: GradientDebugReport) -> Self {
202        self.debug_data = Some(data);
203        self
204    }
205
206    /// Add profiling data
207    pub fn with_profiling_data(mut self, data: ProfilerReport) -> Self {
208        self.profiling_data = Some(data);
209        self
210    }
211
212    /// Generate the report
213    pub fn generate(&self) -> Result<Report, ReportError> {
214        let start_time = std::time::Instant::now();
215
216        let sections = match &self.config.report_type {
217            ReportType::DebugReport => self.generate_debug_sections()?,
218            ReportType::PerformanceReport => self.generate_performance_sections()?,
219            ReportType::TrainingReport => self.generate_training_sections()?,
220            ReportType::GradientReport => self.generate_gradient_sections()?,
221            ReportType::MemoryReport => self.generate_memory_sections()?,
222            ReportType::ComprehensiveReport => self.generate_comprehensive_sections()?,
223            ReportType::CustomReport(section_types) => {
224                self.generate_custom_sections(section_types)?
225            },
226        };
227
228        let visualizations = if self.config.include_visualizations {
229            self.generate_visualizations()?
230        } else {
231            HashMap::new()
232        };
233
234        let raw_data = if self.config.include_raw_data {
235            self.generate_raw_data()?
236        } else {
237            HashMap::new()
238        };
239
240        let generation_time = start_time.elapsed().as_secs_f64() * 1000.0;
241
242        let report = Report {
243            metadata: ReportMetadata {
244                title: self.config.title.clone(),
245                subtitle: self.config.subtitle.clone(),
246                author: self.config.author.clone(),
247                organization: self.config.organization.clone(),
248                version: "1.0".to_string(),
249                generation_time_ms: generation_time,
250                additional_metadata: self.config.metadata.clone(),
251            },
252            sections,
253            visualizations,
254            raw_data,
255            generated_at: Utc::now(),
256        };
257
258        Ok(report)
259    }
260
261    /// Generate debug report sections
262    fn generate_debug_sections(&self) -> Result<Vec<GeneratedSection>, ReportError> {
263        let mut sections = Vec::new();
264
265        // Summary section
266        sections.push(self.generate_summary_section()?);
267
268        // Architecture section
269        sections.push(self.generate_architecture_section()?);
270
271        // Gradient analysis
272        if self.debug_data.is_some() {
273            sections.push(self.generate_gradients_section()?);
274        }
275
276        // Error analysis
277        sections.push(self.generate_errors_section()?);
278
279        // Recommendations
280        sections.push(self.generate_recommendations_section()?);
281
282        Ok(sections)
283    }
284
285    /// Generate performance report sections
286    fn generate_performance_sections(&self) -> Result<Vec<GeneratedSection>, ReportError> {
287        let mut sections = Vec::new();
288
289        sections.push(self.generate_summary_section()?);
290        sections.push(self.generate_performance_section()?);
291
292        if self.profiling_data.is_some() {
293            sections.push(self.generate_memory_section()?);
294        }
295
296        sections.push(self.generate_recommendations_section()?);
297
298        Ok(sections)
299    }
300
301    /// Generate training report sections
302    fn generate_training_sections(&self) -> Result<Vec<GeneratedSection>, ReportError> {
303        let mut sections = Vec::new();
304
305        sections.push(self.generate_summary_section()?);
306        sections.push(self.generate_training_section()?);
307        sections.push(self.generate_gradients_section()?);
308        sections.push(self.generate_recommendations_section()?);
309
310        Ok(sections)
311    }
312
313    /// Generate gradient report sections
314    fn generate_gradient_sections(&self) -> Result<Vec<GeneratedSection>, ReportError> {
315        let mut sections = Vec::new();
316
317        sections.push(self.generate_summary_section()?);
318        sections.push(self.generate_gradients_section()?);
319        sections.push(self.generate_recommendations_section()?);
320
321        Ok(sections)
322    }
323
324    /// Generate memory report sections
325    fn generate_memory_sections(&self) -> Result<Vec<GeneratedSection>, ReportError> {
326        let mut sections = Vec::new();
327
328        sections.push(self.generate_summary_section()?);
329        sections.push(self.generate_memory_section()?);
330        sections.push(self.generate_recommendations_section()?);
331
332        Ok(sections)
333    }
334
335    /// Generate comprehensive report sections
336    fn generate_comprehensive_sections(&self) -> Result<Vec<GeneratedSection>, ReportError> {
337        let mut sections = Vec::new();
338
339        sections.push(self.generate_summary_section()?);
340        sections.push(self.generate_architecture_section()?);
341        sections.push(self.generate_performance_section()?);
342        sections.push(self.generate_memory_section()?);
343        sections.push(self.generate_gradients_section()?);
344        sections.push(self.generate_training_section()?);
345        sections.push(self.generate_errors_section()?);
346        sections.push(self.generate_recommendations_section()?);
347
348        Ok(sections)
349    }
350
351    /// Generate custom report sections
352    fn generate_custom_sections(
353        &self,
354        section_types: &[ReportSection],
355    ) -> Result<Vec<GeneratedSection>, ReportError> {
356        let mut sections = Vec::new();
357
358        for section_type in section_types {
359            let section = match section_type {
360                ReportSection::Summary => self.generate_summary_section()?,
361                ReportSection::Architecture => self.generate_architecture_section()?,
362                ReportSection::Performance => self.generate_performance_section()?,
363                ReportSection::Memory => self.generate_memory_section()?,
364                ReportSection::Gradients => self.generate_gradients_section()?,
365                ReportSection::Training => self.generate_training_section()?,
366                ReportSection::Errors => self.generate_errors_section()?,
367                ReportSection::Recommendations => self.generate_recommendations_section()?,
368                ReportSection::Visualizations => self.generate_visualizations_section()?,
369                ReportSection::RawData => self.generate_raw_data_section()?,
370            };
371            sections.push(section);
372        }
373
374        Ok(sections)
375    }
376
377    /// Generate summary section
378    fn generate_summary_section(&self) -> Result<GeneratedSection, ReportError> {
379        let mut content = String::new();
380        let mut data = HashMap::new();
381
382        content.push_str("## Executive Summary\n\n");
383        content.push_str("This report provides a comprehensive analysis of the TrustformeRS model debugging session.\n\n");
384
385        // Add key metrics
386        if let Some(debug_data) = &self.debug_data {
387            content.push_str(&format!(
388                "- **Total Layers Analyzed**: {}\n",
389                debug_data.flow_analysis.layer_analyses.len()
390            ));
391
392            let healthy_layers = debug_data
393                .flow_analysis
394                .layer_analyses
395                .iter()
396                .filter(|(_name, l)| !l.is_vanishing && !l.is_exploding)
397                .count();
398            content.push_str(&format!("- **Healthy Layers**: {}\n", healthy_layers));
399
400            data.insert(
401                "total_layers".to_string(),
402                serde_json::json!(debug_data.flow_analysis.layer_analyses.len()),
403            );
404            data.insert(
405                "healthy_layers".to_string(),
406                serde_json::json!(healthy_layers),
407            );
408        }
409
410        if let Some(profiling_data) = &self.profiling_data {
411            content.push_str(&format!(
412                "- **Total Memory Usage**: {:.2} MB\n",
413                profiling_data.memory_efficiency.peak_memory_mb
414            ));
415            content.push_str(&format!(
416                "- **Execution Time**: {:.2} ms\n",
417                profiling_data.total_runtime.as_millis() as f64
418            ));
419
420            data.insert(
421                "peak_memory_mb".to_string(),
422                serde_json::json!(profiling_data.memory_efficiency.peak_memory_mb),
423            );
424            data.insert(
425                "total_time_ms".to_string(),
426                serde_json::json!(profiling_data.total_runtime.as_millis() as f64),
427            );
428        }
429
430        Ok(GeneratedSection {
431            section_type: ReportSection::Summary,
432            title: "Executive Summary".to_string(),
433            content,
434            data,
435            visualizations: Vec::new(),
436        })
437    }
438
439    /// Generate architecture section
440    fn generate_architecture_section(&self) -> Result<GeneratedSection, ReportError> {
441        let mut content = String::new();
442        let data = HashMap::new();
443
444        content.push_str("## Model Architecture Analysis\n\n");
445        content.push_str("This section provides detailed analysis of the model architecture.\n\n");
446
447        // Add architecture details if available
448        content.push_str("### Layer Structure\n\n");
449        if let Some(debug_data) = &self.debug_data {
450            content.push_str("| Layer | Type | Parameters | Health Status |\n");
451            content.push_str("|-------|------|------------|---------------|\n");
452
453            for (i, (layer_name, layer)) in
454                debug_data.flow_analysis.layer_analyses.iter().enumerate()
455            {
456                let health = if layer.is_vanishing {
457                    "Vanishing"
458                } else if layer.is_exploding {
459                    "Exploding"
460                } else {
461                    "Healthy"
462                };
463                content.push_str(&format!(
464                    "| {} | {} | {} | {} |\n",
465                    i,
466                    layer_name,
467                    "N/A", // In a real implementation, get parameter count
468                    health
469                ));
470            }
471        } else {
472            content.push_str("No architecture data available.\n");
473        }
474
475        Ok(GeneratedSection {
476            section_type: ReportSection::Architecture,
477            title: "Model Architecture Analysis".to_string(),
478            content,
479            data,
480            visualizations: vec!["architecture_diagram".to_string()],
481        })
482    }
483
484    /// Generate performance section
485    fn generate_performance_section(&self) -> Result<GeneratedSection, ReportError> {
486        let mut content = String::new();
487        let mut data = HashMap::new();
488
489        content.push_str("## Performance Analysis\n\n");
490
491        if let Some(profiling_data) = &self.profiling_data {
492            content.push_str("### Timing Statistics\n\n");
493            content.push_str(&format!(
494                "- **Total Execution Time**: {:.2} ms\n",
495                profiling_data.total_runtime.as_millis() as f64
496            ));
497            content.push_str(&format!(
498                "- **Forward Pass Time**: {:.2} ms\n",
499                profiling_data.total_runtime.as_millis() as f64 * 0.6
500            )); // Approximate 60% forward
501            content.push_str(&format!(
502                "- **Backward Pass Time**: {:.2} ms\n",
503                profiling_data.total_runtime.as_millis() as f64 * 0.4
504            )); // Approximate 40% backward
505
506            content.push_str("\n### Throughput\n\n");
507            let tokens_per_sec = 1000.0 / (profiling_data.total_runtime.as_millis() as f64 + 1.0); // Approximate throughput
508            content.push_str(&format!("- **Tokens per Second**: {:.2}\n", tokens_per_sec));
509            content.push_str(&format!(
510                "- **Samples per Second**: {:.2}\n",
511                tokens_per_sec * 10.0
512            )); // Approximate samples
513
514            // Add data for charts
515            let timing_stats = serde_json::json!({
516                "total_time_ms": profiling_data.total_runtime.as_millis() as f64,
517                "forward_pass_ms": profiling_data.total_runtime.as_millis() as f64 * 0.6,
518                "backward_pass_ms": profiling_data.total_runtime.as_millis() as f64 * 0.4
519            });
520            let throughput_stats = serde_json::json!({
521                "tokens_per_second": tokens_per_sec,
522                "samples_per_second": tokens_per_sec * 10.0
523            });
524            data.insert("timing_stats".to_string(), timing_stats);
525            data.insert("throughput_stats".to_string(), throughput_stats);
526        } else {
527            content.push_str("No performance data available.\n");
528        }
529
530        Ok(GeneratedSection {
531            section_type: ReportSection::Performance,
532            title: "Performance Analysis".to_string(),
533            content,
534            data,
535            visualizations: vec!["performance_chart".to_string()],
536        })
537    }
538
539    /// Generate memory section
540    fn generate_memory_section(&self) -> Result<GeneratedSection, ReportError> {
541        let mut content = String::new();
542        let mut data = HashMap::new();
543
544        content.push_str("## Memory Analysis\n\n");
545
546        if let Some(profiling_data) = &self.profiling_data {
547            content.push_str("### Memory Usage\n\n");
548            content.push_str(&format!(
549                "- **Peak Memory**: {:.2} MB\n",
550                profiling_data.memory_efficiency.peak_memory_mb
551            ));
552            content.push_str(&format!(
553                "- **Current Memory**: {:.2} MB\n",
554                profiling_data.memory_efficiency.avg_memory_mb
555            ));
556            content.push_str(&format!(
557                "- **Memory Efficiency**: {:.2}%\n",
558                profiling_data.memory_efficiency.efficiency_score
559            ));
560
561            data.insert(
562                "memory_stats".to_string(),
563                serde_json::to_value(&profiling_data.memory_efficiency)
564                    .map_err(|e| ReportError::SerializationError(e.to_string()))?,
565            );
566        } else {
567            content.push_str("No memory data available.\n");
568        }
569
570        Ok(GeneratedSection {
571            section_type: ReportSection::Memory,
572            title: "Memory Analysis".to_string(),
573            content,
574            data,
575            visualizations: vec!["memory_chart".to_string()],
576        })
577    }
578
579    /// Generate gradients section
580    fn generate_gradients_section(&self) -> Result<GeneratedSection, ReportError> {
581        let mut content = String::new();
582        let mut data = HashMap::new();
583
584        content.push_str("## Gradient Analysis\n\n");
585
586        if let Some(debug_data) = &self.debug_data {
587            content.push_str("### Gradient Health Summary\n\n");
588
589            let healthy_count = debug_data
590                .flow_analysis
591                .layer_analyses
592                .iter()
593                .filter(|(_name, l)| !l.is_vanishing && !l.is_exploding)
594                .count();
595            let problematic_count = debug_data.flow_analysis.layer_analyses.len() - healthy_count;
596
597            content.push_str(&format!("- **Healthy Layers**: {}\n", healthy_count));
598            content.push_str(&format!(
599                "- **Problematic Layers**: {}\n",
600                problematic_count
601            ));
602
603            if problematic_count > 0 {
604                content.push_str("\n### Issues Detected\n\n");
605                for (i, (layer_name, layer)) in
606                    debug_data.flow_analysis.layer_analyses.iter().enumerate()
607                {
608                    if layer.is_vanishing || layer.is_exploding {
609                        let status = if layer.is_vanishing {
610                            "Vanishing gradients"
611                        } else {
612                            "Exploding gradients"
613                        };
614                        content
615                            .push_str(&format!("- **Layer {}** ({}): {}\n", i, layer_name, status));
616                    }
617                }
618            }
619
620            data.insert(
621                "gradient_analysis".to_string(),
622                serde_json::to_value(debug_data)
623                    .map_err(|e| ReportError::SerializationError(e.to_string()))?,
624            );
625        } else {
626            content.push_str("No gradient data available.\n");
627        }
628
629        Ok(GeneratedSection {
630            section_type: ReportSection::Gradients,
631            title: "Gradient Analysis".to_string(),
632            content,
633            data,
634            visualizations: vec!["gradient_flow_chart".to_string()],
635        })
636    }
637
638    /// Generate training section
639    fn generate_training_section(&self) -> Result<GeneratedSection, ReportError> {
640        let content =
641            "## Training Dynamics\n\nTraining dynamics analysis would go here.".to_string();
642        let data = HashMap::new();
643
644        Ok(GeneratedSection {
645            section_type: ReportSection::Training,
646            title: "Training Dynamics".to_string(),
647            content,
648            data,
649            visualizations: vec!["training_curves".to_string()],
650        })
651    }
652
653    /// Generate errors section
654    fn generate_errors_section(&self) -> Result<GeneratedSection, ReportError> {
655        let content = "## Error Analysis\n\nError analysis would go here.".to_string();
656        let data = HashMap::new();
657
658        Ok(GeneratedSection {
659            section_type: ReportSection::Errors,
660            title: "Error Analysis".to_string(),
661            content,
662            data,
663            visualizations: Vec::new(),
664        })
665    }
666
667    /// Generate recommendations section
668    fn generate_recommendations_section(&self) -> Result<GeneratedSection, ReportError> {
669        let mut content = String::new();
670        let data = HashMap::new();
671
672        content.push_str("## Recommendations\n\n");
673        content.push_str("Based on the analysis, here are our recommendations:\n\n");
674
675        // Add specific recommendations based on data
676        if let Some(debug_data) = &self.debug_data {
677            let problematic_layers = debug_data
678                .flow_analysis
679                .layer_analyses
680                .iter()
681                .filter(|(_name, l)| l.is_vanishing || l.is_exploding)
682                .count();
683
684            if problematic_layers > 0 {
685                content.push_str("### Gradient Issues\n\n");
686                content.push_str("- Consider adjusting learning rate\n");
687                content.push_str("- Review gradient clipping settings\n");
688                content.push_str("- Check for numerical instabilities\n\n");
689            }
690        }
691
692        if let Some(profiling_data) = &self.profiling_data {
693            if profiling_data.memory_efficiency.efficiency_score < 80.0 {
694                content.push_str("### Memory Optimization\n\n");
695                content.push_str("- Consider using gradient checkpointing\n");
696                content.push_str("- Review batch size settings\n");
697                content.push_str("- Consider model quantization\n\n");
698            }
699        }
700
701        content.push_str("### General Recommendations\n\n");
702        content.push_str("- Monitor training regularly\n");
703        content.push_str("- Validate on diverse test sets\n");
704        content.push_str("- Keep detailed training logs\n");
705
706        Ok(GeneratedSection {
707            section_type: ReportSection::Recommendations,
708            title: "Recommendations".to_string(),
709            content,
710            data,
711            visualizations: Vec::new(),
712        })
713    }
714
715    /// Generate visualizations section
716    fn generate_visualizations_section(&self) -> Result<GeneratedSection, ReportError> {
717        let content = "## Visualizations\n\nVisualization section content.".to_string();
718        let data = HashMap::new();
719
720        Ok(GeneratedSection {
721            section_type: ReportSection::Visualizations,
722            title: "Visualizations".to_string(),
723            content,
724            data,
725            visualizations: vec!["all_charts".to_string()],
726        })
727    }
728
729    /// Generate raw data section
730    fn generate_raw_data_section(&self) -> Result<GeneratedSection, ReportError> {
731        let content = "## Raw Data\n\nRaw data section content.".to_string();
732        let data = HashMap::new();
733
734        Ok(GeneratedSection {
735            section_type: ReportSection::RawData,
736            title: "Raw Data".to_string(),
737            content,
738            data,
739            visualizations: Vec::new(),
740        })
741    }
742
743    /// Generate visualizations
744    fn generate_visualizations(&self) -> Result<HashMap<String, PlotData>, ReportError> {
745        let mut visualizations = HashMap::new();
746
747        // Generate performance chart
748        if self.profiling_data.is_some() {
749            let plot_data = PlotData {
750                x_values: vec![1.0, 2.0, 3.0],    // Placeholder data
751                y_values: vec![10.0, 15.0, 12.0], // Placeholder performance data
752                labels: vec!["A".to_string(), "B".to_string(), "C".to_string()],
753                title: "Performance Chart".to_string(),
754                x_label: "Time".to_string(),
755                y_label: "Performance".to_string(),
756            };
757            visualizations.insert("performance_chart".to_string(), plot_data);
758        }
759
760        Ok(visualizations)
761    }
762
763    /// Generate raw data
764    fn generate_raw_data(&self) -> Result<HashMap<String, serde_json::Value>, ReportError> {
765        let mut raw_data = HashMap::new();
766
767        if let Some(debug_data) = &self.debug_data {
768            raw_data.insert(
769                "debug_data".to_string(),
770                serde_json::to_value(debug_data)
771                    .map_err(|e| ReportError::SerializationError(e.to_string()))?,
772            );
773        }
774
775        if let Some(profiling_data) = &self.profiling_data {
776            raw_data.insert(
777                "profiling_data".to_string(),
778                serde_json::to_value(profiling_data)
779                    .map_err(|e| ReportError::SerializationError(e.to_string()))?,
780            );
781        }
782
783        Ok(raw_data)
784    }
785
786    /// Export report to file
787    pub fn export_report(&self, report: &Report) -> Result<(), ReportError> {
788        match self.config.format {
789            ReportFormat::Html => self.export_html(report),
790            ReportFormat::Markdown => self.export_markdown(report),
791            ReportFormat::Json => self.export_json(report),
792            ReportFormat::Pdf => self.export_pdf(report),
793            ReportFormat::Jupyter => self.export_jupyter(report),
794            ReportFormat::Latex => self.export_latex(report),
795            ReportFormat::Excel => self.export_excel(report),
796            ReportFormat::PowerPoint => self.export_powerpoint(report),
797        }
798    }
799
800    /// Export to HTML format
801    fn export_html(&self, report: &Report) -> Result<(), ReportError> {
802        let mut html = String::new();
803
804        html.push_str("<!DOCTYPE html>\n<html>\n<head>\n");
805        html.push_str(&format!("<title>{}</title>\n", report.metadata.title));
806        html.push_str("<style>body { font-family: Arial, sans-serif; margin: 40px; }</style>\n");
807        html.push_str("</head>\n<body>\n");
808
809        html.push_str(&format!("<h1>{}</h1>\n", report.metadata.title));
810        if let Some(subtitle) = &report.metadata.subtitle {
811            html.push_str(&format!("<h2>{}</h2>\n", subtitle));
812        }
813
814        for section in &report.sections {
815            html.push_str(&section.content);
816        }
817
818        html.push_str("</body>\n</html>");
819
820        std::fs::write(format!("{}.html", self.config.output_path), html)
821            .map_err(|e| ReportError::FileError(e.to_string()))?;
822
823        Ok(())
824    }
825
826    /// Export to Markdown format
827    fn export_markdown(&self, report: &Report) -> Result<(), ReportError> {
828        let mut markdown = String::new();
829
830        markdown.push_str(&format!("# {}\n\n", report.metadata.title));
831        if let Some(subtitle) = &report.metadata.subtitle {
832            markdown.push_str(&format!("## {}\n\n", subtitle));
833        }
834
835        markdown.push_str(&format!("**Author**: {}\n", report.metadata.author));
836        markdown.push_str(&format!(
837            "**Generated**: {}\n\n",
838            report.generated_at.format("%Y-%m-%d %H:%M:%S UTC")
839        ));
840
841        for section in &report.sections {
842            markdown.push_str(&section.content);
843            markdown.push('\n');
844        }
845
846        std::fs::write(format!("{}.md", self.config.output_path), markdown)
847            .map_err(|e| ReportError::FileError(e.to_string()))?;
848
849        Ok(())
850    }
851
852    /// Export to JSON format
853    fn export_json(&self, report: &Report) -> Result<(), ReportError> {
854        let json = serde_json::to_string_pretty(report)
855            .map_err(|e| ReportError::SerializationError(e.to_string()))?;
856
857        std::fs::write(format!("{}.json", self.config.output_path), json)
858            .map_err(|e| ReportError::FileError(e.to_string()))?;
859
860        Ok(())
861    }
862
863    /// Export to PDF format (placeholder implementation)
864    fn export_pdf(&self, _report: &Report) -> Result<(), ReportError> {
865        // In a real implementation, this would use a PDF generation library
866        Err(ReportError::UnsupportedFormat(
867            "PDF export not implemented".to_string(),
868        ))
869    }
870
871    /// Export to Jupyter notebook format
872    fn export_jupyter(&self, report: &Report) -> Result<(), ReportError> {
873        let mut notebook = serde_json::json!({
874            "cells": [],
875            "metadata": {
876                "kernelspec": {
877                    "display_name": "Python 3",
878                    "language": "python",
879                    "name": "python3"
880                }
881            },
882            "nbformat": 4,
883            "nbformat_minor": 4
884        });
885
886        // Add title cell
887        let title_cell = serde_json::json!({
888            "cell_type": "markdown",
889            "metadata": {},
890            "source": [format!("# {}\n\n**Generated**: {}",
891                report.metadata.title,
892                report.generated_at.format("%Y-%m-%d %H:%M:%S UTC"))]
893        });
894        let cells = notebook["cells"].as_array_mut().ok_or_else(|| {
895            ReportError::SerializationError("notebook cells should be an array".to_string())
896        })?;
897        cells.push(title_cell);
898
899        // Add content cells
900        for section in &report.sections {
901            let cell = serde_json::json!({
902                "cell_type": "markdown",
903                "metadata": {},
904                "source": [section.content]
905            });
906            cells.push(cell);
907        }
908
909        let notebook_str = serde_json::to_string_pretty(&notebook)
910            .map_err(|e| ReportError::SerializationError(e.to_string()))?;
911
912        std::fs::write(format!("{}.ipynb", self.config.output_path), notebook_str)
913            .map_err(|e| ReportError::FileError(e.to_string()))?;
914
915        Ok(())
916    }
917
918    /// Export to LaTeX format
919    fn export_latex(&self, report: &Report) -> Result<(), ReportError> {
920        let mut latex = String::new();
921
922        latex.push_str("\\documentclass{article}\n");
923        latex.push_str("\\begin{document}\n");
924        latex.push_str(&format!("\\title{{{}}}\n", report.metadata.title));
925        latex.push_str(&format!("\\author{{{}}}\n", report.metadata.author));
926        latex.push_str("\\maketitle\n\n");
927
928        for section in &report.sections {
929            // Convert markdown to LaTeX (simplified)
930            let latex_content = section
931                .content
932                .replace("##", "\\section{")
933                .replace("###", "\\subsection{")
934                .replace("#", "\\section{");
935            latex.push_str(&latex_content);
936        }
937
938        latex.push_str("\\end{document}\n");
939
940        std::fs::write(format!("{}.tex", self.config.output_path), latex)
941            .map_err(|e| ReportError::FileError(e.to_string()))?;
942
943        Ok(())
944    }
945
946    /// Export to Excel format (placeholder)
947    fn export_excel(&self, _report: &Report) -> Result<(), ReportError> {
948        Err(ReportError::UnsupportedFormat(
949            "Excel export not implemented".to_string(),
950        ))
951    }
952
953    /// Export to PowerPoint format (placeholder)
954    fn export_powerpoint(&self, _report: &Report) -> Result<(), ReportError> {
955        Err(ReportError::UnsupportedFormat(
956            "PowerPoint export not implemented".to_string(),
957        ))
958    }
959}
960
961/// Report generation errors
962#[derive(Debug, Clone)]
963pub enum ReportError {
964    /// File system error
965    FileError(String),
966    /// Serialization error
967    SerializationError(String),
968    /// Unsupported format
969    UnsupportedFormat(String),
970    /// Missing data
971    MissingData(String),
972    /// Generation error
973    GenerationError(String),
974}
975
976impl std::fmt::Display for ReportError {
977    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
978        match self {
979            ReportError::FileError(msg) => write!(f, "File error: {}", msg),
980            ReportError::SerializationError(msg) => write!(f, "Serialization error: {}", msg),
981            ReportError::UnsupportedFormat(msg) => write!(f, "Unsupported format: {}", msg),
982            ReportError::MissingData(msg) => write!(f, "Missing data: {}", msg),
983            ReportError::GenerationError(msg) => write!(f, "Generation error: {}", msg),
984        }
985    }
986}
987
988impl std::error::Error for ReportError {}
989
990#[cfg(test)]
991mod tests {
992    use super::*;
993
994    #[test]
995    fn test_report_config_default() {
996        let config = ReportConfig::default();
997        assert_eq!(config.title, "TrustformeRS Debug Report");
998        assert_eq!(config.author, "TrustformeRS Debugger");
999        assert!(matches!(config.format, ReportFormat::Html));
1000        assert!(matches!(
1001            config.report_type,
1002            ReportType::ComprehensiveReport
1003        ));
1004    }
1005
1006    #[test]
1007    fn test_report_generator_creation() {
1008        let config = ReportConfig::default();
1009        let generator = ReportGenerator::new(config);
1010        assert!(generator.debug_data.is_none());
1011        assert!(generator.profiling_data.is_none());
1012    }
1013
1014    #[test]
1015    fn test_report_generation() {
1016        let config = ReportConfig {
1017            title: "Test Report".to_string(),
1018            format: ReportFormat::Json,
1019            report_type: ReportType::DebugReport,
1020            ..Default::default()
1021        };
1022
1023        let generator = ReportGenerator::new(config);
1024        let report = generator.generate().expect("operation failed in test");
1025
1026        assert_eq!(report.metadata.title, "Test Report");
1027        assert!(!report.sections.is_empty());
1028    }
1029
1030    #[test]
1031    fn test_section_generation() {
1032        let config = ReportConfig::default();
1033        let generator = ReportGenerator::new(config);
1034
1035        let summary = generator.generate_summary_section().expect("operation failed in test");
1036        assert!(matches!(summary.section_type, ReportSection::Summary));
1037        assert_eq!(summary.title, "Executive Summary");
1038        assert!(!summary.content.is_empty());
1039    }
1040
1041    #[test]
1042    fn test_custom_report_type() {
1043        let config = ReportConfig {
1044            report_type: ReportType::CustomReport(vec![
1045                ReportSection::Summary,
1046                ReportSection::Performance,
1047            ]),
1048            ..Default::default()
1049        };
1050
1051        let generator = ReportGenerator::new(config);
1052        let report = generator.generate().expect("operation failed in test");
1053
1054        assert_eq!(report.sections.len(), 2);
1055        assert!(matches!(
1056            report.sections[0].section_type,
1057            ReportSection::Summary
1058        ));
1059        assert!(matches!(
1060            report.sections[1].section_type,
1061            ReportSection::Performance
1062        ));
1063    }
1064
1065    #[test]
1066    fn test_report_serialization() {
1067        let config = ReportConfig::default();
1068        let generator = ReportGenerator::new(config);
1069        let report = generator.generate().expect("operation failed in test");
1070
1071        let json = serde_json::to_string(&report).expect("JSON serialization failed");
1072        let deserialized: Report =
1073            serde_json::from_str(&json).expect("JSON deserialization failed");
1074
1075        assert_eq!(report.metadata.title, deserialized.metadata.title);
1076        assert_eq!(report.sections.len(), deserialized.sections.len());
1077    }
1078}