html/
html.rs

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('<', "&lt;").replace('>', "&gt;")
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}