diff options
Diffstat (limited to '')
-rw-r--r-- | askama_shared/Cargo.toml | 2 | ||||
-rw-r--r-- | askama_shared/src/filters/mod.rs | 56 | ||||
-rw-r--r-- | askama_shared/src/generator.rs | 41 |
3 files changed, 96 insertions, 3 deletions
diff --git a/askama_shared/Cargo.toml b/askama_shared/Cargo.toml index b75ec77..e19ad35 100644 --- a/askama_shared/Cargo.toml +++ b/askama_shared/Cargo.toml @@ -13,10 +13,12 @@ edition = "2018" default = ["config", "humansize", "num-traits", "percent-encoding"] config = ["serde", "toml"] json = ["serde", "serde_json"] +markdown = ["comrak"] yaml = ["serde", "serde_yaml"] [dependencies] askama_escape = { version = "0.10.2", path = "../askama_escape" } +comrak = { version = "0.12", optional = true, default-features = false } humansize = { version = "1.1.0", optional = true } mime = "0.3" mime_guess = "2" diff --git a/askama_shared/src/filters/mod.rs b/askama_shared/src/filters/mod.rs index 35bdd5a..9fb10d2 100644 --- a/askama_shared/src/filters/mod.rs +++ b/askama_shared/src/filters/mod.rs @@ -47,7 +47,7 @@ const URLENCODE_SET: &AsciiSet = &URLENCODE_STRICT_SET.remove(b'/'); // Askama or should refer to a local `filters` module. It should contain all the // filters shipped with Askama, even the optional ones (since optional inclusion // in the const vector based on features seems impossible right now). -pub const BUILT_IN_FILTERS: [&str; 27] = [ +pub const BUILT_IN_FILTERS: &[&str] = &[ "abs", "capitalize", "center", @@ -73,8 +73,10 @@ pub const BUILT_IN_FILTERS: [&str; 27] = [ "urlencode", "urlencode_strict", "wordcount", - "json", // Optional feature; reserve the name anyway - "yaml", // Optional feature; reserve the name anyway + // optional features, reserve the names anyway: + "json", + "markdown", + "yaml", ]; /// Marks a string (or other `Display` type) as safe @@ -379,6 +381,54 @@ pub fn wordcount<T: fmt::Display>(s: T) -> Result<usize> { Ok(s.split_whitespace().count()) } +#[cfg(feature = "markdown")] +pub fn markdown<E, S>( + e: E, + s: S, + options: Option<&comrak::ComrakOptions>, +) -> Result<MarkupDisplay<E, String>> +where + E: Escaper, + S: AsRef<str>, +{ + use comrak::{ + markdown_to_html, ComrakExtensionOptions, ComrakOptions, ComrakParseOptions, + ComrakRenderOptions, + }; + + const DEFAULT_OPTIONS: ComrakOptions = ComrakOptions { + extension: ComrakExtensionOptions { + strikethrough: true, + tagfilter: true, + table: true, + autolink: true, + // default: + tasklist: false, + superscript: false, + header_ids: None, + footnotes: false, + description_lists: false, + front_matter_delimiter: None, + }, + parse: ComrakParseOptions { + // default: + smart: false, + default_info_string: None, + }, + render: ComrakRenderOptions { + unsafe_: false, + escape: true, + // default: + hardbreaks: false, + github_pre_lang: false, + width: 0, + }, + }; + + let s = markdown_to_html(s.as_ref(), options.unwrap_or(&DEFAULT_OPTIONS)); + Ok(MarkupDisplay::new_safe(s, e)) +} + #[cfg(test)] mod tests { use super::*; diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs index 63a0154..c3beb88 100644 --- a/askama_shared/src/generator.rs +++ b/askama_shared/src/generator.rs @@ -1097,6 +1097,45 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { DisplayWrap::Unwrapped } + #[cfg(not(feature = "markdown"))] + fn _visit_markdown_filter( + &mut self, + _buf: &mut Buffer, + _args: &[Expr<'_>], + ) -> Result<DisplayWrap, CompileError> { + Err("the `markdown` filter requires the `markdown` feature to be enabled".into()) + } + + #[cfg(feature = "markdown")] + fn _visit_markdown_filter( + &mut self, + buf: &mut Buffer, + args: &[Expr<'_>], + ) -> Result<DisplayWrap, CompileError> { + let (md, options) = match args { + [md] => (md, None), + [md, options] => (md, Some(options)), + _ => return Err("markdown filter expects no more than one option argument".into()), + }; + + buf.write(&format!( + "::askama::filters::markdown({}, ", + self.input.escaper + )); + self.visit_expr(buf, md)?; + match options { + Some(options) => { + buf.write(", ::core::option::Option::Some("); + self.visit_expr(buf, options)?; + buf.write(")"); + } + None => buf.write(", ::core::option::Option::None"), + } + buf.write(")?"); + + Ok(DisplayWrap::Wrapped) + } + fn visit_filter( &mut self, buf: &mut Buffer, @@ -1115,6 +1154,8 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> { } else if name == "join" { self._visit_join_filter(buf, args)?; return Ok(DisplayWrap::Unwrapped); + } else if name == "markdown" { + return self._visit_markdown_filter(buf, args); } if name == "tojson" { |