1use std::{collections::HashMap, path::PathBuf};
2
3use mdtrans::{transform_markdown_string, MarkdownTransformer};
4
5extern crate mdtrans;
6
7#[derive(Default)]
8pub struct Transformer {
9 refs: HashMap<String, String>,
10}
11
12impl Transformer {
13 fn sanitize_html(&self, text: String) -> String {
14 text.replace('<', "<").replace('>', ">")
15 }
16}
17
18impl MarkdownTransformer for Transformer {
19 fn transform_text(&mut self, text: String) -> String {
20 self.sanitize_html(text)
21 }
22
23 fn transform_quote(&mut self, text: String) -> String {
24 format!("<div class=\"quote\">{text}</div>")
25 }
26
27 fn transform_image(
28 &mut self,
29 alt: String,
30 url: String,
31 add_tags: std::collections::HashMap<String, String>,
32 ) -> String {
33 let mut metadata = " ".to_string();
34 metadata += add_tags
35 .into_iter()
36 .map(|(key, val)| {
37 let val = val.trim_start_matches('\"').trim_end_matches('\"');
38 format!(" {key}=\"{val}\"")
39 })
40 .collect::<Vec<String>>()
41 .join(" ")
42 .as_str();
43 format!("<img src=\"{url}\" alt=\"{alt}\"{metadata}>")
44 }
45
46 fn transform_bold(&mut self, text: String) -> String {
47 format!("<strong>{text}</strong>")
48 }
49
50 fn transform_italic(&mut self, text: String) -> String {
51 format!("<em>{text}</em>")
52 }
53
54 fn transform_link(&mut self, text: String, url: String) -> String {
55 format!("<a href=\"{url}\">{text}</a>")
56 }
57
58 fn transform_header(&mut self, level: usize, text: String) -> String {
59 format!("<h{level}>{text}</h{level}>")
60 }
61
62 fn transform_inline_code(&mut self, text: String) -> String {
63 format!("<code>{}</code>", self.sanitize_html(text))
64 }
65
66 fn transform_codeblock(&mut self, lang: Option<String>, text: String) -> String {
67 let mut buffer = "<pre".to_string();
68 if let Some(l) = lang {
69 buffer += format!(" class=\"lang-{}\"", l.to_lowercase()).as_str();
70 }
71 buffer += format!("><code>{}</code></pre>", self.sanitize_html(text)).as_str();
72 buffer
73 }
74
75 fn peek_refurl(&mut self, slug: String, url: String) {
76 self.refs.insert(slug, url);
77 }
78
79 fn transform_reflink(&mut self, text: String, slug: String) -> String {
80 let url = self.refs.get(&slug);
81 assert!(url.is_some(), "Link reference {slug} not found");
82 self.transform_link(text, url.unwrap().clone())
83 }
84
85 fn transform_refurl(&mut self, _slug: String, _url: String) -> String {
86 "".to_string()
87 }
88
89 fn transform_list(&mut self, elements: Vec<String>) -> String {
90 let mut buffer = "<ul>\n".to_string();
91 buffer += elements.join("\n").as_str();
92 buffer += "\n</ul>";
93 buffer
94 }
95
96 fn transform_list_element(&mut self, element: String) -> String {
97 format!("<li>{}</li>", self.sanitize_html(element))
98 }
99
100 fn transform_paragraph(&mut self, text: String) -> String {
101 format!("<p>{text}</p>")
102 }
103
104 fn transform_vertical_space(&mut self) -> String {
105 "<br/>".to_string()
106 }
107}
108
109fn create_page(post: String) -> String {
110 format!(
111 "
112 <!DOCTYPE html>
113 <html lang=\"en\">
114 <head>
115 <meta charset=\"UTF-8\">
116 <title>Post 1</title>
117 </head>
118 {post}
119 </html>
120 "
121 )
122}
123
124fn main() {
125 let mut transformer = Transformer::default();
126
127 for file in std::fs::read_dir("./examples/data").unwrap() {
128 let tstart = std::time::Instant::now();
129 let post_file = file.unwrap().path();
130 let fname = post_file
131 .file_name()
132 .unwrap()
133 .to_os_string()
134 .into_string()
135 .unwrap();
136 if !fname.ends_with(".md") {
137 continue;
138 }
139 println!();
140 let new_fname = fname.replace(".md", ".html");
141 println!("{} -> {}", fname, new_fname);
142 let post = std::fs::read_to_string(&post_file).unwrap();
143 let res = transform_markdown_string(post, &mut transformer).unwrap();
144 std::fs::write(PathBuf::from(new_fname), create_page(res)).unwrap();
145 println!("Done in {:?}", tstart.elapsed());
146 }
147}