use std::collections::HashMap;
use std::time::SystemTime;
use super::config::ReportFormat;
use std::fmt::Write;
pub struct TestReportGenerator {
pub templates: HashMap<String, ReportTemplate>,
pub generated_reports: Vec<GeneratedReport>,
pub config: super::config::ReportingConfig,
}
impl TestReportGenerator {
#[must_use]
pub fn new() -> Self {
Self {
templates: HashMap::new(),
generated_reports: vec![],
config: super::config::ReportingConfig::default(),
}
}
pub fn register_template(&mut self, template: ReportTemplate) {
self.templates.insert(template.name.clone(), template);
}
pub fn generate_report(
&mut self,
template_name: &str,
data: &ReportData,
) -> Result<GeneratedReport, String> {
let template = self
.templates
.get(template_name)
.ok_or_else(|| format!("Template '{template_name}' not found"))?;
let content = match template.format {
ReportFormat::HTML => self.generate_html_report(template, data)?,
ReportFormat::JSON => self.generate_json_report(template, data)?,
ReportFormat::XML => self.generate_xml_report(template, data)?,
ReportFormat::PDF => self.generate_pdf_report(template, data)?,
ReportFormat::CSV => self.generate_csv_report(template, data)?,
};
let report = GeneratedReport {
id: format!(
"report_{}",
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("system time before UNIX_EPOCH")
.as_secs()
),
name: template.name.clone(),
format: template.format.clone(),
generated_at: SystemTime::now(),
content: content.clone(),
metadata: template.metadata.clone(),
size: content.len(),
};
self.generated_reports.push(report.clone());
Ok(report)
}
fn generate_html_report(
&self,
template: &ReportTemplate,
_data: &ReportData,
) -> Result<String, String> {
let mut html = String::from("<html><head><title>");
html.push_str(&template.metadata.title);
html.push_str("</title></head><body>");
for section in &template.sections {
write!(html, "<h2>{}</h2>", section.name).expect("failed to write to string");
match §ion.content {
SectionContent::Text(text) => {
write!(html, "<p>{text}</p>").expect("failed to write to string");
}
SectionContent::Table(_) => {
html.push_str("<table><tr><th>Metric</th><th>Value</th></tr>");
html.push_str("<tr><td>Tests Passed</td><td>100</td></tr>");
html.push_str("</table>");
}
_ => {
html.push_str("<p>Content not implemented</p>");
}
}
}
html.push_str("</body></html>");
Ok(html)
}
fn generate_json_report(
&self,
template: &ReportTemplate,
_data: &ReportData,
) -> Result<String, String> {
let json = format!(
r#"{{"title":"{}","description":"{}","sections":[{{"name":"Summary","content":"Test report"}}]}}"#,
template.metadata.title, template.metadata.description
);
Ok(json)
}
fn generate_xml_report(
&self,
template: &ReportTemplate,
_data: &ReportData,
) -> Result<String, String> {
let mut xml = String::from("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
write!(xml, "<report title=\"{}\">\n", template.metadata.title)
.expect("failed to write to string");
write!(
xml,
" <description>{}</description>\n",
template.metadata.description
)
.expect("failed to write to string");
for section in &template.sections {
write!(xml, " <section name=\"{}\">\n", section.name)
.expect("failed to write to string");
match §ion.content {
SectionContent::Text(text) => {
writeln!(xml, " <content>{text}</content>")
.expect("failed to write to string");
}
SectionContent::Table(_) => {
xml.push_str(" <table>\n");
xml.push_str(" <row><cell>Tests Passed</cell><cell>100</cell></row>\n");
xml.push_str(" </table>\n");
}
_ => {
xml.push_str(" <content>Content not implemented</content>\n");
}
}
xml.push_str(" </section>\n");
}
xml.push_str("</report>");
Ok(xml)
}
fn generate_pdf_report(
&self,
template: &ReportTemplate,
_data: &ReportData,
) -> Result<String, String> {
Ok(format!("PDF Report: {}", template.metadata.title))
}
fn generate_csv_report(
&self,
_template: &ReportTemplate,
_data: &ReportData,
) -> Result<String, String> {
let csv = "Metric,Value\nTests Passed,100\nTests Failed,0\n";
Ok(csv.to_string())
}
#[must_use]
pub fn get_report(&self, report_id: &str) -> Option<&GeneratedReport> {
self.generated_reports.iter().find(|r| r.id == report_id)
}
#[must_use]
pub fn list_reports(&self) -> Vec<&GeneratedReport> {
self.generated_reports.iter().collect()
}
pub fn export_report(&self, report_id: &str, _file_path: &str) -> Result<(), String> {
self.get_report(report_id)
.ok_or_else(|| format!("Report {report_id} not found"))?;
Ok(())
}
pub fn clear_reports(&mut self) {
self.generated_reports.clear();
}
#[must_use]
pub fn report_count(&self) -> usize {
self.generated_reports.len()
}
}
#[derive(Debug, Clone)]
pub struct ReportData {
pub test_results: Vec<super::results::IntegrationTestResult>,
pub performance_metrics: HashMap<String, f64>,
pub additional_data: HashMap<String, String>,
}
#[derive(Debug, Clone)]
pub struct ReportTemplate {
pub name: String,
pub format: ReportFormat,
pub sections: Vec<ReportSection>,
pub metadata: ReportMetadata,
}
#[derive(Debug, Clone)]
pub struct ReportSection {
pub name: String,
pub section_type: SectionType,
pub content: SectionContent,
pub formatting: SectionFormatting,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SectionType {
Summary,
TestResults,
PerformanceMetrics,
ErrorAnalysis,
Recommendations,
Custom(String),
}
#[derive(Debug, Clone)]
pub enum SectionContent {
Text(String),
Data(DataQuery),
Chart(ChartDefinition),
Table(TableDefinition),
Custom(String),
}
#[derive(Debug, Clone)]
pub struct DataQuery {
pub query_type: QueryType,
pub parameters: HashMap<String, String>,
pub transformation: Option<DataTransformation>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum QueryType {
TestResults,
PerformanceMetrics,
ErrorCounts,
TrendData,
ComparisonData,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct DataTransformation {
pub transformation_type: TransformationType,
pub parameters: HashMap<String, String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TransformationType {
Aggregate,
Filter,
Sort,
Group,
Calculate,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct ChartDefinition {
pub chart_type: ChartType,
pub data_source: DataQuery,
pub configuration: ChartConfiguration,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChartType {
Line,
Bar,
Pie,
Scatter,
Histogram,
Heatmap,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct ChartConfiguration {
pub title: String,
pub x_axis_label: String,
pub y_axis_label: String,
pub dimensions: (u32, u32),
pub color_scheme: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct TableDefinition {
pub columns: Vec<TableColumn>,
pub data_source: DataQuery,
pub formatting: TableFormatting,
}
#[derive(Debug, Clone)]
pub struct TableColumn {
pub name: String,
pub column_type: ColumnType,
pub formatting: ColumnFormatting,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ColumnType {
Text,
Number,
DateTime,
Boolean,
Duration,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct ColumnFormatting {
pub number_format: Option<NumberFormat>,
pub date_format: Option<String>,
pub alignment: TextAlignment,
}
#[derive(Debug, Clone)]
pub struct NumberFormat {
pub decimal_places: usize,
pub thousands_separator: bool,
pub unit: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TextAlignment {
Left,
Center,
Right,
}
#[derive(Debug, Clone)]
pub struct TableFormatting {
pub show_headers: bool,
pub alternate_rows: bool,
pub border_style: BorderStyle,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BorderStyle {
None,
Simple,
Double,
Rounded,
Custom(String),
}
#[derive(Debug, Clone)]
pub struct SectionFormatting {
pub font_size: u8,
pub font_weight: FontWeight,
pub text_color: String,
pub background_color: Option<String>,
pub padding: (u8, u8, u8, u8),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum FontWeight {
Normal,
Bold,
Light,
ExtraBold,
}
#[derive(Debug, Clone)]
pub struct GeneratedReport {
pub id: String,
pub name: String,
pub format: ReportFormat,
pub generated_at: SystemTime,
pub content: String,
pub metadata: ReportMetadata,
pub size: usize,
}
#[derive(Debug, Clone)]
pub struct ReportMetadata {
pub title: String,
pub description: String,
pub author: String,
pub version: String,
pub custom: HashMap<String, String>,
}