1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use comrak::{markdown_to_html, ComrakOptions};
use rlua::prelude::*;
use std::collections::HashMap;
const HARDBREAKS: &str = "hardbreaks";
const SMART: &str = "smart";
const GITHUB_PRE_LANG: &str = "github_pre_lang";
const WIDTH: &str = "width";
const DEFAULT_INFO_STRING: &str = "default_info_string";
const UNSAFE: &str = "unsafe";
const EXT_STRIKETHROUGH: &str = "ext_strikethrough";
const EXT_TAGFILTER: &str = "ext_tagfilter";
const EXT_TABLE: &str = "ext_table";
const EXT_AUTOLINK: &str = "ext_autolink";
const EXT_TASKLIST: &str = "ext_tasklist";
const EXT_SUPERSCRIPT: &str = "ext_superscript";
const EXT_HEADER_IDS: &str = "ext_header_ids";
const EXT_FOOTNOTES: &str = "ext_footnotes";
fn comrak_options_from_table(table: &HashMap<String, LuaValue>) -> Result<ComrakOptions, LuaError> {
let mut options = ComrakOptions {
..ComrakOptions::default()
};
for (k, v) in table.iter() {
match (k.as_str(), v) {
(HARDBREAKS, LuaValue::Boolean(val)) => options.hardbreaks = *val,
(SMART, LuaValue::Boolean(val)) => options.smart = *val,
(GITHUB_PRE_LANG, LuaValue::Boolean(val)) => options.smart = *val,
(WIDTH, LuaValue::Integer(val)) => options.width = *val as usize,
(DEFAULT_INFO_STRING, LuaValue::String(val)) => {
options.default_info_string = Some(val.to_str()?.to_string())
}
(UNSAFE, LuaValue::Boolean(val)) => options.unsafe_ = *val,
(EXT_STRIKETHROUGH, LuaValue::Boolean(val)) => options.ext_strikethrough = *val,
(EXT_TAGFILTER, LuaValue::Boolean(val)) => options.ext_tagfilter = *val,
(EXT_TABLE, LuaValue::Boolean(val)) => options.ext_table = *val,
(EXT_AUTOLINK, LuaValue::Boolean(val)) => options.ext_autolink = *val,
(EXT_TASKLIST, LuaValue::Boolean(val)) => options.ext_tasklist = *val,
(EXT_SUPERSCRIPT, LuaValue::Boolean(val)) => options.ext_superscript = *val,
(EXT_HEADER_IDS, LuaValue::String(val)) => {
options.ext_header_ids = Some(val.to_str()?.to_string())
}
(EXT_FOOTNOTES, LuaValue::Boolean(val)) => options.ext_footnotes = *val,
(k, v) => unimplemented!(
"Unknown option {:?}, with value {:?}, passed to markdown_to_html",
k,
v
),
}
}
Ok(options)
}
pub fn init(lua: &Lua) -> crate::Result<()> {
let render_markdown = lua.create_function(
|_, (markdown_str, options): (String, Option<HashMap<String, LuaValue>>)| {
let html_string = match options {
Some(options) => {
let opts = comrak_options_from_table(&options)?;
markdown_to_html(&markdown_str, &opts)
}
None => markdown_to_html(&markdown_str, &ComrakOptions::default()),
};
Ok(html_string)
},
)?;
lua.globals().set("markdown_to_html", render_markdown)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_markdown_to_html() {
let lua = Lua::new();
init(&lua).unwrap();
let result = lua.exec::<_, LuaValue>(r#"return markdown_to_html("Hello, **世界**!")"#, None);
match result {
Ok(LuaValue::String(html)) => {
assert_eq!(html, "<p>Hello, <strong>世界</strong>!</p>\n")
}
_ => unimplemented!("Unexpected value returned from markdown_to_html"),
}
}
#[test]
fn test_markdown_to_html_with_options() {
let lua = Lua::new();
init(&lua).unwrap();
let result = lua.exec::<_, LuaValue>(
r#"return markdown_to_html("Hello, **世界**!<script></script>", {unsafe = false})"#,
None,
);
match result {
Ok(LuaValue::String(html)) => {
let s = html.to_str().unwrap().to_string();
assert!(s.contains("<p>Hello, <strong>世界</strong>!"));
assert!(!s.contains("<script>"));
}
_ => unimplemented!("Unexpected value returned from markdown_to_html"),
}
}
}