oxihuman_export/
html_report_export.rs1#![allow(dead_code)]
4
5#[derive(Debug, Clone)]
8pub struct HtmlBodyReport {
9 pub title: String,
10 pub measurements: Vec<(String, String, String)>, pub notes: Vec<String>,
12}
13
14pub fn new_html_body_report(title: &str) -> HtmlBodyReport {
15 HtmlBodyReport {
16 title: title.to_string(),
17 measurements: Vec::new(),
18 notes: Vec::new(),
19 }
20}
21
22pub fn add_measurement(report: &mut HtmlBodyReport, name: &str, value: f32, unit: &str) {
23 report
24 .measurements
25 .push((name.to_string(), format!("{:.2}", value), unit.to_string()));
26}
27
28pub fn add_note(report: &mut HtmlBodyReport, note: &str) {
29 report.notes.push(note.to_string());
30}
31
32fn html_escape_str(s: &str) -> String {
33 s.replace('&', "&")
34 .replace('<', "<")
35 .replace('>', ">")
36 .replace('"', """)
37}
38
39pub fn render_html_report(report: &HtmlBodyReport) -> String {
40 let mut html = format!(
41 "<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>{}</title></head><body>\n",
42 html_escape_str(&report.title)
43 );
44 html.push_str(&format!("<h1>{}</h1>\n", html_escape_str(&report.title)));
45 html.push_str("<table border=\"1\"><tr><th>Measurement</th><th>Value</th><th>Unit</th></tr>\n");
46 for (name, value, unit) in &report.measurements {
47 html.push_str(&format!(
48 "<tr><td>{}</td><td>{}</td><td>{}</td></tr>\n",
49 html_escape_str(name),
50 html_escape_str(value),
51 html_escape_str(unit)
52 ));
53 }
54 html.push_str("</table>\n");
55 if !report.notes.is_empty() {
56 html.push_str("<ul>\n");
57 for note in &report.notes {
58 html.push_str(&format!("<li>{}</li>\n", html_escape_str(note)));
59 }
60 html.push_str("</ul>\n");
61 }
62 html.push_str("</body></html>\n");
63 html
64}
65
66pub fn export_html_body_report(report: &HtmlBodyReport) -> Vec<u8> {
67 render_html_report(report).into_bytes()
68}
69
70pub fn measurement_count(report: &HtmlBodyReport) -> usize {
71 report.measurements.len()
72}
73pub fn note_count(report: &HtmlBodyReport) -> usize {
74 report.notes.len()
75}
76pub fn validate_html_report(report: &HtmlBodyReport) -> bool {
77 !report.title.is_empty()
78}
79pub fn html_report_size_bytes(report: &HtmlBodyReport) -> usize {
80 render_html_report(report).len()
81}
82
83pub fn default_html_body_report(height_cm: f32, weight_kg: f32) -> HtmlBodyReport {
84 let mut r = new_html_body_report("Body Report");
85 add_measurement(&mut r, "Height", height_cm, "cm");
86 add_measurement(&mut r, "Weight", weight_kg, "kg");
87 r
88}
89
90#[cfg(test)]
91mod tests {
92 use super::*;
93
94 #[test]
95 fn test_new_html_body_report() {
96 let r = new_html_body_report("Test");
97 assert_eq!(r.title, "Test");
98 }
99
100 #[test]
101 fn test_add_measurement() {
102 let mut r = new_html_body_report("T");
103 add_measurement(&mut r, "Height", 170.0, "cm");
104 assert_eq!(measurement_count(&r), 1);
105 }
106
107 #[test]
108 fn test_render_contains_doctype() {
109 let r = new_html_body_report("X");
110 let html = render_html_report(&r);
111 assert!(html.contains("<!DOCTYPE html>"));
112 }
113
114 #[test]
115 fn test_render_contains_title() {
116 let r = new_html_body_report("MyTitle");
117 let html = render_html_report(&r);
118 assert!(html.contains("MyTitle"));
119 }
120
121 #[test]
122 fn test_export_bytes_nonempty() {
123 let r = new_html_body_report("T");
124 assert!(!export_html_body_report(&r).is_empty());
125 }
126
127 #[test]
128 fn test_validate_html_report() {
129 let r = new_html_body_report("V");
130 assert!(validate_html_report(&r));
131 }
132
133 #[test]
134 fn test_note_count() {
135 let mut r = new_html_body_report("T");
136 add_note(&mut r, "Note 1");
137 assert_eq!(note_count(&r), 1);
138 }
139
140 #[test]
141 fn test_default_html_body_report() {
142 let r = default_html_body_report(165.0, 55.0);
143 assert_eq!(measurement_count(&r), 2);
144 }
145}