1use std::collections::HashMap;
4use std::time::SystemTime;
5
6use super::config::ReportFormat;
7
8use std::fmt::Write;
9pub struct TestReportGenerator {
11 pub templates: HashMap<String, ReportTemplate>,
13 pub generated_reports: Vec<GeneratedReport>,
15 pub config: super::config::ReportingConfig,
17}
18
19impl TestReportGenerator {
20 #[must_use]
21 pub fn new() -> Self {
22 Self {
23 templates: HashMap::new(),
24 generated_reports: vec![],
25 config: super::config::ReportingConfig::default(),
26 }
27 }
28
29 pub fn register_template(&mut self, template: ReportTemplate) {
31 self.templates.insert(template.name.clone(), template);
32 }
33
34 pub fn generate_report(
36 &mut self,
37 template_name: &str,
38 data: &ReportData,
39 ) -> Result<GeneratedReport, String> {
40 let template = self
41 .templates
42 .get(template_name)
43 .ok_or_else(|| format!("Template '{template_name}' not found"))?;
44
45 let content = match template.format {
47 ReportFormat::HTML => self.generate_html_report(template, data)?,
48 ReportFormat::JSON => self.generate_json_report(template, data)?,
49 ReportFormat::XML => self.generate_xml_report(template, data)?,
50 ReportFormat::PDF => self.generate_pdf_report(template, data)?,
51 ReportFormat::CSV => self.generate_csv_report(template, data)?,
52 };
53
54 let report = GeneratedReport {
55 id: format!(
56 "report_{}",
57 SystemTime::now()
58 .duration_since(SystemTime::UNIX_EPOCH)
59 .expect("system time before UNIX_EPOCH")
60 .as_secs()
61 ),
62 name: template.name.clone(),
63 format: template.format.clone(),
64 generated_at: SystemTime::now(),
65 content: content.clone(),
66 metadata: template.metadata.clone(),
67 size: content.len(),
68 };
69
70 self.generated_reports.push(report.clone());
71 Ok(report)
72 }
73
74 fn generate_html_report(
76 &self,
77 template: &ReportTemplate,
78 _data: &ReportData,
79 ) -> Result<String, String> {
80 let mut html = String::from("<html><head><title>");
81 html.push_str(&template.metadata.title);
82 html.push_str("</title></head><body>");
83
84 for section in &template.sections {
85 write!(html, "<h2>{}</h2>", section.name).expect("failed to write to string");
86 match §ion.content {
87 SectionContent::Text(text) => {
88 write!(html, "<p>{text}</p>").expect("failed to write to string");
89 }
90 SectionContent::Table(_) => {
91 html.push_str("<table><tr><th>Metric</th><th>Value</th></tr>");
92 html.push_str("<tr><td>Tests Passed</td><td>100</td></tr>");
93 html.push_str("</table>");
94 }
95 _ => {
96 html.push_str("<p>Content not implemented</p>");
97 }
98 }
99 }
100
101 html.push_str("</body></html>");
102 Ok(html)
103 }
104
105 fn generate_json_report(
107 &self,
108 template: &ReportTemplate,
109 _data: &ReportData,
110 ) -> Result<String, String> {
111 let json = format!(
112 r#"{{"title":"{}","description":"{}","sections":[{{"name":"Summary","content":"Test report"}}]}}"#,
113 template.metadata.title, template.metadata.description
114 );
115 Ok(json)
116 }
117
118 fn generate_xml_report(
120 &self,
121 template: &ReportTemplate,
122 _data: &ReportData,
123 ) -> Result<String, String> {
124 let mut xml = String::from("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
125 write!(xml, "<report title=\"{}\">\n", template.metadata.title)
126 .expect("failed to write to string");
127 write!(
128 xml,
129 " <description>{}</description>\n",
130 template.metadata.description
131 )
132 .expect("failed to write to string");
133
134 for section in &template.sections {
135 write!(xml, " <section name=\"{}\">\n", section.name)
136 .expect("failed to write to string");
137 match §ion.content {
138 SectionContent::Text(text) => {
139 writeln!(xml, " <content>{text}</content>")
140 .expect("failed to write to string");
141 }
142 SectionContent::Table(_) => {
143 xml.push_str(" <table>\n");
144 xml.push_str(" <row><cell>Tests Passed</cell><cell>100</cell></row>\n");
145 xml.push_str(" </table>\n");
146 }
147 _ => {
148 xml.push_str(" <content>Content not implemented</content>\n");
149 }
150 }
151 xml.push_str(" </section>\n");
152 }
153
154 xml.push_str("</report>");
155 Ok(xml)
156 }
157
158 fn generate_pdf_report(
160 &self,
161 template: &ReportTemplate,
162 _data: &ReportData,
163 ) -> Result<String, String> {
164 Ok(format!("PDF Report: {}", template.metadata.title))
165 }
166
167 fn generate_csv_report(
169 &self,
170 _template: &ReportTemplate,
171 _data: &ReportData,
172 ) -> Result<String, String> {
173 let csv = "Metric,Value\nTests Passed,100\nTests Failed,0\n";
174 Ok(csv.to_string())
175 }
176
177 #[must_use]
179 pub fn get_report(&self, report_id: &str) -> Option<&GeneratedReport> {
180 self.generated_reports.iter().find(|r| r.id == report_id)
181 }
182
183 #[must_use]
185 pub fn list_reports(&self) -> Vec<&GeneratedReport> {
186 self.generated_reports.iter().collect()
187 }
188
189 pub fn export_report(&self, report_id: &str, _file_path: &str) -> Result<(), String> {
191 self.get_report(report_id)
192 .ok_or_else(|| format!("Report {report_id} not found"))?;
193 Ok(())
194 }
195
196 pub fn clear_reports(&mut self) {
198 self.generated_reports.clear();
199 }
200
201 #[must_use]
203 pub fn report_count(&self) -> usize {
204 self.generated_reports.len()
205 }
206}
207
208#[derive(Debug, Clone)]
210pub struct ReportData {
211 pub test_results: Vec<super::results::IntegrationTestResult>,
213 pub performance_metrics: HashMap<String, f64>,
215 pub additional_data: HashMap<String, String>,
217}
218
219#[derive(Debug, Clone)]
221pub struct ReportTemplate {
222 pub name: String,
224 pub format: ReportFormat,
226 pub sections: Vec<ReportSection>,
228 pub metadata: ReportMetadata,
230}
231
232#[derive(Debug, Clone)]
234pub struct ReportSection {
235 pub name: String,
237 pub section_type: SectionType,
239 pub content: SectionContent,
241 pub formatting: SectionFormatting,
243}
244
245#[derive(Debug, Clone, PartialEq, Eq)]
247pub enum SectionType {
248 Summary,
249 TestResults,
250 PerformanceMetrics,
251 ErrorAnalysis,
252 Recommendations,
253 Custom(String),
254}
255
256#[derive(Debug, Clone)]
258pub enum SectionContent {
259 Text(String),
261 Data(DataQuery),
263 Chart(ChartDefinition),
265 Table(TableDefinition),
267 Custom(String),
269}
270
271#[derive(Debug, Clone)]
273pub struct DataQuery {
274 pub query_type: QueryType,
276 pub parameters: HashMap<String, String>,
278 pub transformation: Option<DataTransformation>,
280}
281
282#[derive(Debug, Clone, PartialEq, Eq)]
284pub enum QueryType {
285 TestResults,
286 PerformanceMetrics,
287 ErrorCounts,
288 TrendData,
289 ComparisonData,
290 Custom(String),
291}
292
293#[derive(Debug, Clone)]
295pub struct DataTransformation {
296 pub transformation_type: TransformationType,
298 pub parameters: HashMap<String, String>,
300}
301
302#[derive(Debug, Clone, PartialEq, Eq)]
304pub enum TransformationType {
305 Aggregate,
306 Filter,
307 Sort,
308 Group,
309 Calculate,
310 Custom(String),
311}
312
313#[derive(Debug, Clone)]
315pub struct ChartDefinition {
316 pub chart_type: ChartType,
318 pub data_source: DataQuery,
320 pub configuration: ChartConfiguration,
322}
323
324#[derive(Debug, Clone, PartialEq, Eq)]
326pub enum ChartType {
327 Line,
328 Bar,
329 Pie,
330 Scatter,
331 Histogram,
332 Heatmap,
333 Custom(String),
334}
335
336#[derive(Debug, Clone)]
338pub struct ChartConfiguration {
339 pub title: String,
341 pub x_axis_label: String,
343 pub y_axis_label: String,
345 pub dimensions: (u32, u32),
347 pub color_scheme: Vec<String>,
349}
350
351#[derive(Debug, Clone)]
353pub struct TableDefinition {
354 pub columns: Vec<TableColumn>,
356 pub data_source: DataQuery,
358 pub formatting: TableFormatting,
360}
361
362#[derive(Debug, Clone)]
364pub struct TableColumn {
365 pub name: String,
367 pub column_type: ColumnType,
369 pub formatting: ColumnFormatting,
371}
372
373#[derive(Debug, Clone, PartialEq, Eq)]
375pub enum ColumnType {
376 Text,
377 Number,
378 DateTime,
379 Boolean,
380 Duration,
381 Custom(String),
382}
383
384#[derive(Debug, Clone)]
386pub struct ColumnFormatting {
387 pub number_format: Option<NumberFormat>,
389 pub date_format: Option<String>,
391 pub alignment: TextAlignment,
393}
394
395#[derive(Debug, Clone)]
397pub struct NumberFormat {
398 pub decimal_places: usize,
400 pub thousands_separator: bool,
402 pub unit: Option<String>,
404}
405
406#[derive(Debug, Clone, PartialEq, Eq)]
408pub enum TextAlignment {
409 Left,
410 Center,
411 Right,
412}
413
414#[derive(Debug, Clone)]
416pub struct TableFormatting {
417 pub show_headers: bool,
419 pub alternate_rows: bool,
421 pub border_style: BorderStyle,
423}
424
425#[derive(Debug, Clone, PartialEq, Eq)]
427pub enum BorderStyle {
428 None,
429 Simple,
430 Double,
431 Rounded,
432 Custom(String),
433}
434
435#[derive(Debug, Clone)]
437pub struct SectionFormatting {
438 pub font_size: u8,
440 pub font_weight: FontWeight,
442 pub text_color: String,
444 pub background_color: Option<String>,
446 pub padding: (u8, u8, u8, u8),
448}
449
450#[derive(Debug, Clone, PartialEq, Eq)]
452pub enum FontWeight {
453 Normal,
454 Bold,
455 Light,
456 ExtraBold,
457}
458
459#[derive(Debug, Clone)]
461pub struct GeneratedReport {
462 pub id: String,
464 pub name: String,
466 pub format: ReportFormat,
468 pub generated_at: SystemTime,
470 pub content: String,
472 pub metadata: ReportMetadata,
474 pub size: usize,
476}
477
478#[derive(Debug, Clone)]
480pub struct ReportMetadata {
481 pub title: String,
483 pub description: String,
485 pub author: String,
487 pub version: String,
489 pub custom: HashMap<String, String>,
491}