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();
                //                println!("html = {}", s);
                assert!(s.contains("<p>Hello, <strong>世界</strong>!"));
                assert!(!s.contains("<script>"));
            }
            _ => unimplemented!("Unexpected value returned from markdown_to_html"),
        }
    }
}