Packages
mdex
0.1.6
0.13.3
0.13.2
0.13.1
0.13.0
0.12.5
retired
0.12.4
0.12.3
0.12.2
0.12.1
0.12.0
0.11.7
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.6
0.8.5
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.0
0.4.3
0.4.2
0.4.1
0.4.0
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.18
0.1.17
0.1.16
0.1.15
0.1.14
0.1.13
0.1.12
0.1.11
0.1.10
0.1.9
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
Fast and extensible Markdown for Elixir
Current section
Files
Jump to
Current section
Files
native/comrak_nif/src/lib.rs
#[macro_use]
extern crate rustler;
use ammonia::clean;
use comrak::{
markdown_to_html, markdown_to_html_with_plugins, plugins::syntect::SyntectAdapter,
plugins::syntect::SyntectAdapterBuilder, ComrakOptions, ComrakPlugins,
};
use rustler::{Env, NifResult, Term};
use serde_rustler::to_term;
use syntect::highlighting::ThemeSet;
rustler::init!("Elixir.MDEx.Native", [to_html, to_html_with_options]);
#[derive(Debug, NifStruct)]
#[module = "MDEx.ExtensionOptions"]
pub struct ExtensionOptions {
pub strikethrough: bool,
pub tagfilter: bool,
pub table: bool,
pub autolink: bool,
pub tasklist: bool,
pub superscript: bool,
pub header_ids: Option<String>,
pub footnotes: bool,
pub description_lists: bool,
pub front_matter_delimiter: Option<String>,
}
#[derive(Debug, NifStruct)]
#[module = "MDEx.ParseOptions"]
pub struct ParseOptions {
pub smart: bool,
pub default_info_string: Option<String>,
pub relaxed_tasklist_matching: bool,
}
#[derive(Debug, NifUnitEnum)]
pub enum ListStyleType {
Dash,
Plus,
Star,
}
#[derive(Debug, NifStruct)]
#[module = "MDEx.RenderOptions"]
pub struct RenderOptions {
pub hardbreaks: bool,
pub github_pre_lang: bool,
pub full_info_string: bool,
pub width: usize,
pub unsafe_: bool,
pub escape: bool,
pub list_style: ListStyleType,
pub sourcepos: bool,
}
#[derive(Debug, NifStruct)]
#[module = "MDEx.FeaturesOptions"]
pub struct FeaturesOptions {
pub sanitize: bool,
pub syntax_highlight_theme: Option<String>,
}
#[derive(Debug, NifStruct)]
#[module = "MDEx.Options"]
pub struct Options {
pub extension: ExtensionOptions,
pub parse: ParseOptions,
pub render: RenderOptions,
pub features: FeaturesOptions,
}
#[rustler::nif(schedule = "DirtyCpu")]
fn to_html(md: &str) -> String {
let syntec_adapter = SyntectAdapter::new("InspiredGitHub");
let mut plugins = ComrakPlugins::default();
plugins.render.codefence_syntax_highlighter = Some(&syntec_adapter);
markdown_to_html_with_plugins(md, &ComrakOptions::default(), &plugins)
}
#[rustler::nif(schedule = "DirtyCpu")]
fn to_html_with_options<'a>(env: Env<'a>, md: &str, options: Options) -> NifResult<Term<'a>> {
let comrak_options = ComrakOptions {
extension: comrak::ComrakExtensionOptions {
strikethrough: options.extension.strikethrough,
tagfilter: options.extension.tagfilter,
table: options.extension.table,
autolink: options.extension.autolink,
tasklist: options.extension.tasklist,
superscript: options.extension.superscript,
header_ids: options.extension.header_ids,
footnotes: options.extension.footnotes,
description_lists: options.extension.description_lists,
front_matter_delimiter: options.extension.front_matter_delimiter,
},
parse: comrak::ComrakParseOptions {
smart: options.parse.smart,
default_info_string: options.parse.default_info_string,
relaxed_tasklist_matching: options.parse.relaxed_tasklist_matching,
},
render: comrak::ComrakRenderOptions {
hardbreaks: options.render.hardbreaks,
github_pre_lang: options.render.github_pre_lang,
full_info_string: options.render.full_info_string,
width: options.render.width,
unsafe_: options.render.unsafe_,
escape: options.render.escape,
list_style: list_style(options.render.list_style),
sourcepos: options.render.sourcepos,
},
};
match options.features.syntax_highlight_theme {
Some(theme) => {
let mut plugins = ComrakPlugins::default();
let adapter = build_syntect_adapter(theme);
plugins.render.codefence_syntax_highlighter = Some(&adapter);
let unsafe_html = markdown_to_html_with_plugins(md, &comrak_options, &plugins);
render(env, unsafe_html, options.features.sanitize)
}
None => {
let unsafe_html = markdown_to_html(md, &comrak_options);
render(env, unsafe_html, options.features.sanitize)
}
}
}
fn build_syntect_adapter(theme: String) -> SyntectAdapter {
let syntax_set = two_face::syntax::extra_newlines();
let theme_set = two_face::theme::extra();
let syntect_theme_set = ThemeSet::from(&theme_set);
SyntectAdapterBuilder::new()
.syntax_set(syntax_set)
.theme_set(syntect_theme_set)
.theme(theme.as_str())
.build()
}
fn render(env: Env, unsafe_html: String, sanitize: bool) -> NifResult<Term> {
let html = match sanitize {
true => clean(&unsafe_html),
false => unsafe_html,
};
to_term(env, html).map_err(|err| err.into())
}
fn list_style(list_style: ListStyleType) -> comrak::ListStyleType {
match list_style {
ListStyleType::Dash => comrak::ListStyleType::Dash,
ListStyleType::Plus => comrak::ListStyleType::Plus,
ListStyleType::Star => comrak::ListStyleType::Star,
}
}