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