query_forge/output/formats/
html.rs1use crate::{QueryResult, QueryValue};
2use crate::value::display_value;
3
4use super::utils::escape_xml_text;
5
6pub fn render_html(result: &QueryResult) -> String {
7 let mut output = String::from(
8 "<!DOCTYPE html>\n\
9 <html>\n\
10 <head><meta charset=\"UTF-8\"><title>Query Result</title></head>\n\
11 <body>\n\
12 <table>\n\
13 <thead>\n\
14 <tr>\n",
15 );
16
17 for column in &result.columns {
18 output.push_str(" <th>");
19 output.push_str(&escape_xml_text(column));
20 output.push_str("</th>\n");
21 }
22 output.push_str("</tr>\n</thead>\n<tbody>\n");
23
24 for row in &result.rows {
25 output.push_str("<tr>\n");
26 for column_index in 0..result.columns.len() {
27 let value = row.get(column_index).unwrap_or(&QueryValue::Null);
28 output.push_str(" <td>");
29 output.push_str(&escape_xml_text(&display_value(value)));
30 output.push_str("</td>\n");
31 }
32 output.push_str("</tr>\n");
33 }
34
35 output.push_str("</tbody>\n</table>\n</body>\n</html>");
36 output
37}
38
39#[cfg(test)]
40mod tests {
41 use super::render_html;
42 use crate::{QueryResult, QueryValue};
43
44 #[test]
45 fn renders_html_with_title() {
46 let html = render_html(&QueryResult {
47 columns: vec!["product".to_owned()],
48 rows: vec![vec![QueryValue::Text("Keyboard".to_owned())]],
49 });
50
51 assert!(html.contains("<title>Query Result</title>"));
52 }
53}