markdown_to_html/
translator.rs1use 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
60fn 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}