markdown_to_html/
translator.rs

1use crate::Markdown;
2use crate::MarkdownInline;
3use crate::MarkdownText;
4
5pub fn translate(md: Vec<Markdown>) -> String {
6    md.iter()
7        .map(|bit| match bit {
8            Markdown::Heading(size, line) => translate_header(*size, line.to_vec()),
9            Markdown::UnorderedList(lines) => translate_unordered_list(lines.to_vec()),
10            Markdown::OrderedList(lines) => translate_ordered_list(lines.to_vec()),
11            Markdown::Codeblock(lang, code) => {
12                translate_codeblock(lang.to_string(), code.to_string())
13            }
14            Markdown::Line(line) => translate_line(line.to_vec()),
15        })
16        .collect::<Vec<String>>()
17        .join("")
18}
19
20fn translate_boldtext(boldtext: String) -> String {
21    format!("<b>{}</b>", boldtext)
22}
23
24fn translate_italic(italic: String) -> String {
25    format!("<i>{}</i>", italic)
26}
27
28fn translate_inline_code(code: String) -> String {
29    format!("<code>{}</code>", code)
30}
31
32fn translate_link(text: String, url: String) -> String {
33    format!("<a href=\"{}\">{}</a>", url, text)
34}
35
36fn translate_image(text: String, url: String) -> String {
37    format!("<img src=\"{}\" alt=\"{}\" />", url, text)
38}
39
40fn translate_list_elements(lines: Vec<MarkdownText>) -> String {
41    lines
42        .iter()
43        .map(|line| format!("<li>{}</li>", translate_text(line.to_vec())))
44        .collect::<Vec<String>>()
45        .join("")
46}
47
48fn translate_header(size: usize, text: MarkdownText) -> String {
49    format!("<h{}>{}</h{}>", size, translate_text(text), size)
50}
51
52fn translate_unordered_list(lines: Vec<MarkdownText>) -> String {
53    format!("<ul>{}</ul>", translate_list_elements(lines.to_vec()))
54}
55
56fn translate_ordered_list(lines: Vec<MarkdownText>) -> String {
57    format!("<ol>{}</ol>", translate_list_elements(lines.to_vec()))
58}
59
60// fn translate_code(code: MarkdownText) -> String {
61//     format!("<code>{}</code>", translate_text(code))
62// }
63
64fn translate_codeblock(lang: String, code: String) -> String {
65    format!("<pre><code class=\"lang-{}\">{}</code></pre>", lang, code)
66}
67
68fn translate_line(text: MarkdownText) -> String {
69    let line = translate_text(text);
70    if line.len() > 0 {
71        format!("<p>{}</p>", line)
72    } else {
73        format!("{}", line)
74    }
75}
76
77fn translate_text(text: MarkdownText) -> String {
78    text.iter()
79        .map(|part| match part {
80            MarkdownInline::Bold(text) => translate_boldtext(text.to_string()),
81            MarkdownInline::Italic(text) => translate_italic(text.to_string()),
82            MarkdownInline::InlineCode(code) => translate_inline_code(code.to_string()),
83            MarkdownInline::Link(text, url) => translate_link(text.to_string(), url.to_string()),
84            MarkdownInline::Image(text, url) => translate_image(text.to_string(), url.to_string()),
85            MarkdownInline::Plaintext(text) => text.to_string(),
86        })
87        .collect::<Vec<String>>()
88        .join("")
89}
90
91#[cfg(test)]
92mod tests {
93    use super::*;
94
95    #[test]
96    fn test_translate_boldtext() {
97        assert_eq!(
98            translate_boldtext(String::from("bold af")),
99            String::from("<b>bold af</b>")
100        );
101    }
102
103    #[test]
104    fn test_translate_italic() {
105        assert_eq!(
106            translate_italic(String::from("italic af")),
107            String::from("<i>italic af</i>")
108        );
109    }
110
111    #[test]
112    fn test_translate_inline_code() {
113        assert_eq!(
114            translate_inline_code(String::from("code af")),
115            String::from("<code>code af</code>")
116        );
117    }
118
119    #[test]
120    fn test_translate_link() {
121        assert_eq!(
122            translate_link(
123                String::from("click me!"),
124                String::from("https://github.com")
125            ),
126            String::from("<a href=\"https://github.com\">click me!</a>")
127        );
128    }
129
130    #[test]
131    fn test_translate_image() {
132        assert_eq!(
133            translate_image(String::from("alt text"), String::from("https://github.com")),
134            String::from("<img src=\"https://github.com\" alt=\"alt text\" />")
135        );
136    }
137
138    #[test]
139    fn test_translate_text() {
140        let x = translate_text(vec![
141            MarkdownInline::Plaintext(String::from(
142                "Foobar is a Python library for dealing with word pluralization.",
143            )),
144            MarkdownInline::Bold(String::from("bold")),
145            MarkdownInline::Italic(String::from("italic")),
146            MarkdownInline::InlineCode(String::from("code")),
147            MarkdownInline::Link(String::from("tag"), String::from("https://link.com")),
148            MarkdownInline::Image(String::from("tag"), String::from("https://link.com")),
149            MarkdownInline::Plaintext(String::from(". the end!")),
150        ]);
151        assert_eq!(x, String::from("Foobar is a Python library for dealing with word pluralization.<b>bold</b><i>italic</i><code>code</code><a href=\"https://link.com\">tag</a><img src=\"https://link.com\" alt=\"tag\" />. the end!"));
152        let x = translate_text(vec![]);
153        assert_eq!(x, String::from(""));
154    }
155
156    #[test]
157    fn test_translate_header() {
158        assert_eq!(
159            translate_header(1, vec![MarkdownInline::Plaintext(String::from("Foobar"))]),
160            String::from("<h1>Foobar</h1>")
161        );
162    }
163
164    #[test]
165    fn test_translate_list_elements() {
166        assert_eq!(
167            translate_list_elements(vec![
168                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
169                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
170                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
171                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
172            ]),
173            String::from("<li>Foobar</li><li>Foobar</li><li>Foobar</li><li>Foobar</li>")
174        );
175    }
176
177    #[test]
178    fn test_translate_unordered_list() {
179        assert_eq!(
180            translate_unordered_list(vec![
181                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
182                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
183                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
184                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
185            ]),
186            String::from("<ul><li>Foobar</li><li>Foobar</li><li>Foobar</li><li>Foobar</li></ul>")
187        );
188    }
189
190    #[test]
191    fn test_translate_ordered_list() {
192        assert_eq!(
193            translate_ordered_list(vec![
194                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
195                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
196                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
197                vec![MarkdownInline::Plaintext(String::from("Foobar"))],
198            ]),
199            String::from("<ol><li>Foobar</li><li>Foobar</li><li>Foobar</li><li>Foobar</li></ol>")
200        );
201    }
202
203    #[test]
204    fn test_translate_codeblock() {
205        assert_eq!(
206            translate_codeblock(
207                String::from("python"),
208                String::from(
209                    r#"
210import foobar
211
212foobar.pluralize(\'word\') # returns \'words\'
213foobar.pluralize(\'goose\') # returns \'geese\'
214foobar.singularize(\'phenomena\') # returns \'phenomenon\'
215"#
216                )
217            ),
218            String::from(
219                r#"<pre><code class="lang-python">
220import foobar
221
222foobar.pluralize(\'word\') # returns \'words\'
223foobar.pluralize(\'goose\') # returns \'geese\'
224foobar.singularize(\'phenomena\') # returns \'phenomenon\'
225</code></pre>"#
226            )
227        );
228    }
229
230    #[test]
231    fn test_translate_line() {
232        assert_eq!(
233            translate_line(vec![
234                MarkdownInline::Plaintext(String::from("Foobar")),
235                MarkdownInline::Bold(String::from("Foobar")),
236                MarkdownInline::Italic(String::from("Foobar")),
237                MarkdownInline::InlineCode(String::from("Foobar")),
238            ]),
239            String::from("<p>Foobar<b>Foobar</b><i>Foobar</i><code>Foobar</code></p>")
240        );
241    }
242}