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).unwrap(),
561            );
562        } else {
563            content.push_str("No memory data available.\n");
564        }
565
566        Ok(GeneratedSection {
567            section_type: ReportSection::Memory,
568            title: "Memory Analysis".to_string(),
569            content,
570            data,
571            visualizations: vec!["memory_chart".to_string()],
572        })
573    }
574
575    /// Generate gradients section
576    fn generate_gradients_section(&self) -> Result<GeneratedSection, ReportError> {
577        let mut content = String::new();
578        let mut data = HashMap::new();
579
580        content.push_str("## Gradient Analysis\n\n");
581
582        if let Some(debug_data) = &self.debug_data {
583            content.push_str("### Gradient Health Summary\n\n");
584
585            let healthy_count = debug_data
586                .flow_analysis
587                .layer_analyses
588                .iter()
589                .filter(|(_name, l)| !l.is_vanishing && !l.is_exploding)
590                .count();
591            let problematic_count = debug_data.flow_analysis.layer_analyses.len() - healthy_count;
592
593            content.push_str(&format!("- **Healthy Layers**: {}\n", healthy_count));
594            content.push_str(&format!(
595                "- **Problematic Layers**: {}\n",
596                problematic_count
597            ));
598
599            if problematic_count > 0 {
600                content.push_str("\n### Issues Detected\n\n");
601                for (i, (layer_name, layer)) in
602                    debug_data.flow_analysis.layer_analyses.iter().enumerate()
603                {
604                    if layer.is_vanishing || layer.is_exploding {
605                        let status = if layer.is_vanishing {
606                            "Vanishing gradients"
607                        } else {
608                            "Exploding gradients"
609                        };
610                        content
611                            .push_str(&format!("- **Layer {}** ({}): {}\n", i, layer_name, status));
612                    }
613                }
614            }
615
616            data.insert(
617                "gradient_analysis".to_string(),
618                serde_json::to_value(debug_data).unwrap(),
619            );
620        } else {
621            content.push_str("No gradient data available.\n");
622        }
623
624        Ok(GeneratedSection {
625            section_type: ReportSection::Gradients,
626            title: "Gradient Analysis".to_string(),
627            content,
628            data,
629            visualizations: vec!["gradient_flow_chart".to_string()],
630        })
631    }
632
633    /// Generate training section
634    fn generate_training_section(&self) -> Result<GeneratedSection, ReportError> {
635        let content =
636            "## Training Dynamics\n\nTraining dynamics analysis would go here.".to_string();
637        let data = HashMap::new();
638
639        Ok(GeneratedSection {
640            section_type: ReportSection::Training,
641            title: "Training Dynamics".to_string(),
642            content,
643            data,
644            visualizations: vec!["training_curves".to_string()],
645        })
646    }
647
648    /// Generate errors section
649    fn generate_errors_section(&self) -> Result<GeneratedSection, ReportError> {
650        let content = "## Error Analysis\n\nError analysis would go here.".to_string();
651        let data = HashMap::new();
652
653        Ok(GeneratedSection {
654            section_type: ReportSection::Errors,
655            title: "Error Analysis".to_string(),
656            content,
657            data,
658            visualizations: Vec::new(),
659        })
660    }
661
662    /// Generate recommendations section
663    fn generate_recommendations_section(&self) -> Result<GeneratedSection, ReportError> {
664        let mut content = String::new();
665        let data = HashMap::new();
666
667        content.push_str("## Recommendations\n\n");
668        content.push_str("Based on the analysis, here are our recommendations:\n\n");
669
670        // Add specific recommendations based on data
671        if let Some(debug_data) = &self.debug_data {
672            let problematic_layers = debug_data
673                .flow_analysis
674                .layer_analyses
675                .iter()
676                .filter(|(_name, l)| l.is_vanishing || l.is_exploding)
677                .count();
678
679            if problematic_layers > 0 {
680                content.push_str("### Gradient Issues\n\n");
681                content.push_str("- Consider adjusting learning rate\n");
682                content.push_str("- Review gradient clipping settings\n");
683                content.push_str("- Check for numerical instabilities\n\n");
684            }
685        }
686
687        if let Some(profiling_data) = &self.profiling_data {
688            if profiling_data.memory_efficiency.efficiency_score < 80.0 {
689                content.push_str("### Memory Optimization\n\n");
690                content.push_str("- Consider using gradient checkpointing\n");
691                content.push_str("- Review batch size settings\n");
692                content.push_str("- Consider model quantization\n\n");
693            }
694        }
695
696        content.push_str("### General Recommendations\n\n");
697        content.push_str("- Monitor training regularly\n");
698        content.push_str("- Validate on diverse test sets\n");
699        content.push_str("- Keep detailed training logs\n");
700
701        Ok(GeneratedSection {
702            section_type: ReportSection::Recommendations,
703            title: "Recommendations".to_string(),
704            content,
705            data,
706            visualizations: Vec::new(),
707        })
708    }
709
710    /// Generate visualizations section
711    fn generate_visualizations_section(&self) -> Result<GeneratedSection, ReportError> {
712        let content = "## Visualizations\n\nVisualization section content.".to_string();
713        let data = HashMap::new();
714
715        Ok(GeneratedSection {
716            section_type: ReportSection::Visualizations,
717            title: "Visualizations".to_string(),
718            content,
719            data,
720            visualizations: vec!["all_charts".to_string()],
721        })
722    }
723
724    /// Generate raw data section
725    fn generate_raw_data_section(&self) -> Result<GeneratedSection, ReportError> {
726        let content = "## Raw Data\n\nRaw data section content.".to_string();
727        let data = HashMap::new();
728
729        Ok(GeneratedSection {
730            section_type: ReportSection::RawData,
731            title: "Raw Data".to_string(),
732            content,
733            data,
734            visualizations: Vec::new(),
735        })
736    }
737
738    /// Generate visualizations
739    fn generate_visualizations(&self) -> Result<HashMap<String, PlotData>, ReportError> {
740        let mut visualizations = HashMap::new();
741
742        // Generate performance chart
743        if self.profiling_data.is_some() {
744            let plot_data = PlotData {
745                x_values: vec![1.0, 2.0, 3.0],    // Placeholder data
746                y_values: vec![10.0, 15.0, 12.0], // Placeholder performance data
747                labels: vec!["A".to_string(), "B".to_string(), "C".to_string()],
748                title: "Performance Chart".to_string(),
749                x_label: "Time".to_string(),
750                y_label: "Performance".to_string(),
751            };
752            visualizations.insert("performance_chart".to_string(), plot_data);
753        }
754
755        Ok(visualizations)
756    }
757
758    /// Generate raw data
759    fn generate_raw_data(&self) -> Result<HashMap<String, serde_json::Value>, ReportError> {
760        let mut raw_data = HashMap::new();
761
762        if let Some(debug_data) = &self.debug_data {
763            raw_data.insert(
764                "debug_data".to_string(),
765                serde_json::to_value(debug_data).unwrap(),
766            );
767        }
768
769        if let Some(profiling_data) = &self.profiling_data {
770            raw_data.insert(
771                "profiling_data".to_string(),
772                serde_json::to_value(profiling_data).unwrap(),
773            );
774        }
775
776        Ok(raw_data)
777    }
778
779    /// Export report to file
780    pub fn export_report(&self, report: &Report) -> Result<(), ReportError> {
781        match self.config.format {
782            ReportFormat::Html => self.export_html(report),
783            ReportFormat::Markdown => self.export_markdown(report),
784            ReportFormat::Json => self.export_json(report),
785            ReportFormat::Pdf => self.export_pdf(report),
786            ReportFormat::Jupyter => self.export_jupyter(report),
787            ReportFormat::Latex => self.export_latex(report),
788            ReportFormat::Excel => self.export_excel(report),
789            ReportFormat::PowerPoint => self.export_powerpoint(report),
790        }
791    }
792
793    /// Export to HTML format
794    fn export_html(&self, report: &Report) -> Result<(), ReportError> {
795        let mut html = String::new();
796
797        html.push_str("<!DOCTYPE html>\n<html>\n<head>\n");
798        html.push_str(&format!("<title>{}</title>\n", report.metadata.title));
799        html.push_str("<style>body { font-family: Arial, sans-serif; margin: 40px; }</style>\n");
800        html.push_str("</head>\n<body>\n");
801
802        html.push_str(&format!("<h1>{}</h1>\n", report.metadata.title));
803        if let Some(subtitle) = &report.metadata.subtitle {
804            html.push_str(&format!("<h2>{}</h2>\n", subtitle));
805        }
806
807        for section in &report.sections {
808            html.push_str(&section.content);
809        }
810
811        html.push_str("</body>\n</html>");
812
813        std::fs::write(format!("{}.html", self.config.output_path), html)
814            .map_err(|e| ReportError::FileError(e.to_string()))?;
815
816        Ok(())
817    }
818
819    /// Export to Markdown format
820    fn export_markdown(&self, report: &Report) -> Result<(), ReportError> {
821        let mut markdown = String::new();
822
823        markdown.push_str(&format!("# {}\n\n", report.metadata.title));
824        if let Some(subtitle) = &report.metadata.subtitle {
825            markdown.push_str(&format!("## {}\n\n", subtitle));
826        }
827
828        markdown.push_str(&format!("**Author**: {}\n", report.metadata.author));
829        markdown.push_str(&format!(
830            "**Generated**: {}\n\n",
831            report.generated_at.format("%Y-%m-%d %H:%M:%S UTC")
832        ));
833
834        for section in &report.sections {
835            markdown.push_str(&section.content);
836            markdown.push('\n');
837        }
838
839        std::fs::write(format!("{}.md", self.config.output_path), markdown)
840            .map_err(|e| ReportError::FileError(e.to_string()))?;
841
842        Ok(())
843    }
844
845    /// Export to JSON format
846    fn export_json(&self, report: &Report) -> Result<(), ReportError> {
847        let json = serde_json::to_string_pretty(report)
848            .map_err(|e| ReportError::SerializationError(e.to_string()))?;
849
850        std::fs::write(format!("{}.json", self.config.output_path), json)
851            .map_err(|e| ReportError::FileError(e.to_string()))?;
852
853        Ok(())
854    }
855
856    /// Export to PDF format (placeholder implementation)
857    fn export_pdf(&self, _report: &Report) -> Result<(), ReportError> {
858        // In a real implementation, this would use a PDF generation library
859        Err(ReportError::UnsupportedFormat(
860            "PDF export not implemented".to_string(),
861        ))
862    }
863
864    /// Export to Jupyter notebook format
865    fn export_jupyter(&self, report: &Report) -> Result<(), ReportError> {
866        let mut notebook = serde_json::json!({
867            "cells": [],
868            "metadata": {
869                "kernelspec": {
870                    "display_name": "Python 3",
871                    "language": "python",
872                    "name": "python3"
873                }
874            },
875            "nbformat": 4,
876            "nbformat_minor": 4
877        });
878
879        // Add title cell
880        let title_cell = serde_json::json!({
881            "cell_type": "markdown",
882            "metadata": {},
883            "source": [format!("# {}\n\n**Generated**: {}",
884                report.metadata.title,
885                report.generated_at.format("%Y-%m-%d %H:%M:%S UTC"))]
886        });
887        notebook["cells"].as_array_mut().unwrap().push(title_cell);
888
889        // Add content cells
890        for section in &report.sections {
891            let cell = serde_json::json!({
892                "cell_type": "markdown",
893                "metadata": {},
894                "source": [section.content]
895            });
896            notebook["cells"].as_array_mut().unwrap().push(cell);
897        }
898
899        let notebook_str = serde_json::to_string_pretty(&notebook)
900            .map_err(|e| ReportError::SerializationError(e.to_string()))?;
901
902        std::fs::write(format!("{}.ipynb", self.config.output_path), notebook_str)
903            .map_err(|e| ReportError::FileError(e.to_string()))?;
904
905        Ok(())
906    }
907
908    /// Export to LaTeX format
909    fn export_latex(&self, report: &Report) -> Result<(), ReportError> {
910        let mut latex = String::new();
911
912        latex.push_str("\\documentclass{article}\n");
913        latex.push_str("\\begin{document}\n");
914        latex.push_str(&format!("\\title{{{}}}\n", report.metadata.title));
915        latex.push_str(&format!("\\author{{{}}}\n", report.metadata.author));
916        latex.push_str("\\maketitle\n\n");
917
918        for section in &report.sections {
919            // Convert markdown to LaTeX (simplified)
920            let latex_content = section
921                .content
922                .replace("##", "\\section{")
923                .replace("###", "\\subsection{")
924                .replace("#", "\\section{");
925            latex.push_str(&latex_content);
926        }
927
928        latex.push_str("\\end{document}\n");
929
930        std::fs::write(format!("{}.tex", self.config.output_path), latex)
931            .map_err(|e| ReportError::FileError(e.to_string()))?;
932
933        Ok(())
934    }
935
936    /// Export to Excel format (placeholder)
937    fn export_excel(&self, _report: &Report) -> Result<(), ReportError> {
938        Err(ReportError::UnsupportedFormat(
939            "Excel export not implemented".to_string(),
940        ))
941    }
942
943    /// Export to PowerPoint format (placeholder)
944    fn export_powerpoint(&self, _report: &Report) -> Result<(), ReportError> {
945        Err(ReportError::UnsupportedFormat(
946            "PowerPoint export not implemented".to_string(),
947        ))
948    }
949}
950
951/// Report generation errors
952#[derive(Debug, Clone)]
953pub enum ReportError {
954    /// File system error
955    FileError(String),
956    /// Serialization error
957    SerializationError(String),
958    /// Unsupported format
959    UnsupportedFormat(String),
960    /// Missing data
961    MissingData(String),
962    /// Generation error
963    GenerationError(String),
964}
965
966impl std::fmt::Display for ReportError {
967    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
968        match self {
969            ReportError::FileError(msg) => write!(f, "File error: {}", msg),
970            ReportError::SerializationError(msg) => write!(f, "Serialization error: {}", msg),
971            ReportError::UnsupportedFormat(msg) => write!(f, "Unsupported format: {}", msg),
972            ReportError::MissingData(msg) => write!(f, "Missing data: {}", msg),
973            ReportError::GenerationError(msg) => write!(f, "Generation error: {}", msg),
974        }
975    }
976}
977
978impl std::error::Error for ReportError {}
979
980#[cfg(test)]
981mod tests {
982    use super::*;
983
984    #[test]
985    fn test_report_config_default() {
986        let config = ReportConfig::default();
987        assert_eq!(config.title, "TrustformeRS Debug Report");
988        assert_eq!(config.author, "TrustformeRS Debugger");
989        assert!(matches!(config.format, ReportFormat::Html));
990        assert!(matches!(
991            config.report_type,
992            ReportType::ComprehensiveReport
993        ));
994    }
995
996    #[test]
997    fn test_report_generator_creation() {
998        let config = ReportConfig::default();
999        let generator = ReportGenerator::new(config);
1000        assert!(generator.debug_data.is_none());
1001        assert!(generator.profiling_data.is_none());
1002    }
1003
1004    #[test]
1005    fn test_report_generation() {
1006        let config = ReportConfig {
1007            title: "Test Report".to_string(),
1008            format: ReportFormat::Json,
1009            report_type: ReportType::DebugReport,
1010            ..Default::default()
1011        };
1012
1013        let generator = ReportGenerator::new(config);
1014        let report = generator.generate().unwrap();
1015
1016        assert_eq!(report.metadata.title, "Test Report");
1017        assert!(!report.sections.is_empty());
1018    }
1019
1020    #[test]
1021    fn test_section_generation() {
1022        let config = ReportConfig::default();
1023        let generator = ReportGenerator::new(config);
1024
1025        let summary = generator.generate_summary_section().unwrap();
1026        assert!(matches!(summary.section_type, ReportSection::Summary));
1027        assert_eq!(summary.title, "Executive Summary");
1028        assert!(!summary.content.is_empty());
1029    }
1030
1031    #[test]
1032    fn test_custom_report_type() {
1033        let config = ReportConfig {
1034            report_type: ReportType::CustomReport(vec![
1035                ReportSection::Summary,
1036                ReportSection::Performance,
1037            ]),
1038            ..Default::default()
1039        };
1040
1041        let generator = ReportGenerator::new(config);
1042        let report = generator.generate().unwrap();
1043
1044        assert_eq!(report.sections.len(), 2);
1045        assert!(matches!(
1046            report.sections[0].section_type,
1047            ReportSection::Summary
1048        ));
1049        assert!(matches!(
1050            report.sections[1].section_type,
1051            ReportSection::Performance
1052        ));
1053    }
1054
1055    #[test]
1056    fn test_report_serialization() {
1057        let config = ReportConfig::default();
1058        let generator = ReportGenerator::new(config);
1059        let report = generator.generate().unwrap();
1060
1061        let json = serde_json::to_string(&report).unwrap();
1062        let deserialized: Report = serde_json::from_str(&json).unwrap();
1063
1064        assert_eq!(report.metadata.title, deserialized.metadata.title);
1065        assert_eq!(report.sections.len(), deserialized.sections.len());
1066    }
1067}