rnotifylib/util/
html.rs

1use crate::message::formatted_detail::{FormattedMessageComponent, FormattedMessageDetail, FormattedString, Style};
2
3pub trait HtmlMessageDetail {
4    fn create_html(&self) -> String;
5}
6
7impl HtmlMessageDetail for FormattedMessageDetail {
8    fn create_html(&self) -> String {
9        formatted_to_html(&self)
10    }
11}
12
13fn formatted_to_html(formatted: &FormattedMessageDetail) -> String {
14    let mut html = String::with_capacity(100);
15    for component in formatted.components() {
16        match component {
17            FormattedMessageComponent::Section(section, formatted_string) => {
18                html.push_str(&format!("<div><h2>{}</h2><p>{}</p></div>",
19                                       escape_html(section),
20                                       parse_formatted(formatted_string)
21                ));
22            }
23            FormattedMessageComponent::Text(formatted_string) => {
24                html.push_str(&format!("<p>{}</p>", parse_formatted(formatted_string)))
25            }
26        }
27    }
28    html
29}
30
31fn escape_html(s: &str) -> String {
32    s.replace('&', "&amp;")
33        .replace('>', "&gt;")
34        .replace('<', "&lt;")
35}
36
37fn parse_formatted(formatted: &Vec<FormattedString>) -> String {
38    let mut html = String::new();
39    for part in formatted {
40        for style in part.get_styles() {
41            let start_tag = match style {
42                Style::Bold => "<b>",
43                Style::Italics => "<i>",
44                Style::Monospace => "<code>",
45                Style::Code { lang: _ } => "<code>",
46            };
47            html.push_str(start_tag);
48        }
49        html.push_str(&escape_html(part.get_string()));
50        for style in part.get_styles() {
51            let end_tag = match style {
52                Style::Bold => "</b>",
53                Style::Italics => "</i>",
54                Style::Monospace => "</code>",
55                Style::Code { lang: _ } => "</code>",
56            };
57            html.push_str(end_tag);
58        }
59    }
60    html
61}
62
63#[cfg(test)]
64mod test {
65    use crate::message::detail_builder::{FormattedStringAppendable, MessageDetailBuilder};
66    use crate::message::MessageDetail::Formatted;
67    use super::*;
68
69    #[test]
70    fn test_html_conversion() {
71        let mut builder = MessageDetailBuilder::new();
72        builder.section("hello world", |body| {
73            body.append_plain("Dear fellow inhabitants. it has come to my attention that ");
74            body.append_styled("you have not been doing your job.", Style::Bold);
75        });
76        builder.text_block(|block| {
77            block.append_plain("That is all.");
78        });
79        let message = builder.build();
80        if let Formatted(formatted_detail) = message {
81            let html = formatted_to_html(&formatted_detail);
82            assert_eq!(html, "<div><h2>hello world</h2><p>Dear fellow inhabitants. it has come to my attention that <b>you have not been doing your job.</b></p></div><p>That is all.</p>")
83        }
84        else {
85            panic!("oops");
86        }
87    }
88}