1use mdbook_core::config::Config;
2use minify::html::minify;
3use pulldown_cmark;
4use regex::Regex;
5
6pub fn remove_script_and_style_comments(content: String) -> String {
7 let script_tag_re = Regex::new(r"(?s)<script.*?>.*?</script>").unwrap();
8 let style_tag_re = Regex::new(r"(?s)<style.*?>.*?</style>").unwrap();
9
10 let block_comment_re = Regex::new(r"(?s)/\*.*?\*/").unwrap();
11 let line_comment_re = Regex::new(r"(?m)//.*?$").unwrap(); let content = script_tag_re
15 .replace_all(&content, |caps: ®ex::Captures| {
16 let script_block = &caps[0];
17 if let Some(body_start) = script_block.find('>') {
18 if let Some(body_end) = script_block.rfind("</script>") {
19 let head = &script_block[..=body_start];
20 let body = &script_block[body_start + 1..body_end];
21 let tail = &script_block[body_end..];
22
23 let body = block_comment_re.replace_all(body, "");
24 let body = line_comment_re.replace_all(&body, "");
25
26 format!("{head}{}{tail}", body.trim())
27 } else {
28 script_block.to_string()
29 }
30 } else {
31 script_block.to_string()
32 }
33 })
34 .to_string();
35
36 let content = style_tag_re
38 .replace_all(&content, |caps: ®ex::Captures| {
39 let style_block = &caps[0];
40 if let Some(body_start) = style_block.find('>') {
41 if let Some(body_end) = style_block.rfind("</style>") {
42 let head = &style_block[..=body_start];
43 let body = &style_block[body_start + 1..body_end];
44 let tail = &style_block[body_end..];
45
46 let body = block_comment_re.replace_all(body, "");
47
48 format!("{head}{}{tail}", body.trim())
49 } else {
50 style_block.to_string()
51 }
52 } else {
53 style_block.to_string()
54 }
55 })
56 .to_string();
57
58 content
59}
60
61pub fn minify_html(content: String) -> String {
62 let result = remove_script_and_style_comments(content);
63 minify(&result)
64}
65
66pub fn render_to_markdown(content: String) -> String {
67 let mut html = String::new();
68 let parser = pulldown_cmark::Parser::new(&content);
69 pulldown_cmark::html::push_html(&mut html, parser);
70 minify_html(html.trim().into())
71}
72
73pub fn get_config_bool(config: &Config, key: &str) -> bool {
74 config
75 .get::<bool>(format!("preprocessor.embedify.{}", key).as_str())
76 .ok()
77 .flatten()
78 .unwrap_or(false)
79}
80
81pub fn get_config_string(config: &Config, key: &str, default: &str) -> String {
82 config
83 .get::<String>(format!("preprocessor.embedify.{}", key).as_str())
84 .ok()
85 .flatten()
86 .unwrap_or_else(|| default.to_string())
87}
88
89pub fn create_embed(name: &str, options: Vec<(&str, &str)>) -> String {
90 let mut option_str = String::new();
91 for (key, value) in options {
92 option_str.push_str(&format!(r#"{}="{}""#, key, value));
93 }
94 format!("{{% embed {} {} %}}", name, option_str)
95}
96
97pub fn create_announcement_banner(config: &Config) -> String {
98 let id = get_config_string(config, "announcement-banner.id", "");
100 let theme = get_config_string(config, "announcement-banner.theme", "default");
101 let message = get_config_string(config, "announcement-banner.message", "");
102
103 create_embed(
104 "announcement-banner",
105 vec![("id", &id), ("theme", &theme), ("message", &message)],
106 )
107}
108
109pub fn create_giscus(config: &Config) -> String {
110 let repo = get_config_string(config, "giscus.repo", "");
112 let repo_id = get_config_string(config, "giscus.repo-id", "");
113 let category = get_config_string(config, "giscus.category", "");
114 let category_id = get_config_string(config, "giscus.category-id", "");
115 let reactions_enabled = get_config_string(config, "giscus.reactions-enabled", "1");
116 let theme = get_config_string(config, "giscus.theme", "book");
117 let lang = get_config_string(config, "giscus.lang", "en");
118 let loading = get_config_string(config, "giscus.loading", "lazy");
119
120 let options = vec![
121 ("repo", repo.as_str()),
122 ("repo-id", repo_id.as_str()),
123 ("category", category.as_str()),
124 ("category-id", category_id.as_str()),
125 ("reactions-enabled", reactions_enabled.as_str()),
126 ("theme", theme.as_str()),
127 ("lang", lang.as_str()),
128 ("loading", loading.as_str()),
129 ];
130
131 create_embed("giscus", options)
132}
133
134pub fn create_footer(config: &Config) -> String {
135 let message = get_config_string(config, "footer.message", "");
137 create_embed("footer", vec![("message", &message)])
138}
139
140pub fn create_scroll_to_top() -> String {
141 create_embed("scroll-to-top", vec![])
142}