//! Public API of micromark.
//!
//! This module exposes [`micromark`][] (and [`micromark_with_options`][]).
//! `micromark` is a safe way to transform (untrusted?) markdown into HTML.
//! `micromark_with_options` allows you to configure how markdown is turned into
//! HTML, such as by allowing dangerous HTML when you trust it.
mod compiler;
mod constant;
mod construct;
mod content;
mod parser;
mod subtokenize;
mod token;
mod tokenizer;
mod unicode;
mod util;
use crate::compiler::compile;
use crate::parser::parse;
/// Type of line endings in markdown.
#[derive(Debug, Default, Clone, PartialEq)]
pub enum LineEnding {
/// Both a carriage return (`\r`) and a line feed (`\n`).
///
/// ## Example
///
/// ```markdown
/// a␍␊
/// b
/// ```
CarriageReturnLineFeed,
/// Sole carriage return (`\r`).
///
/// ## Example
///
/// ```markdown
/// a␍
/// b
/// ```
CarriageReturn,
/// Sole line feed (`\n`).
///
/// ## Example
///
/// ```markdown
/// a␊
/// b
/// ```
#[default]
LineFeed,
}
impl LineEnding {
/// Turn the line ending into a [str].
fn as_str(&self) -> &str {
match self {
LineEnding::CarriageReturnLineFeed => "\r\n",
LineEnding::CarriageReturn => "\r",
LineEnding::LineFeed => "\n",
}
}
/// Turn a string into a line ending.
///
/// ## Panics
///
/// Panics if `code` is not `\r\n`, `\r`, or `\n`.
fn from_str(str: &str) -> LineEnding {
match str {
"\r\n" => LineEnding::CarriageReturnLineFeed,
"\r" => LineEnding::CarriageReturn,
"\n" => LineEnding::LineFeed,
_ => unreachable!("invalid code"),
}
}
}
/// Control which constructs are enabled.
///
/// Not all constructs can be configured.
/// Notably, blank lines and paragraphs cannot be turned off.
#[allow(clippy::struct_excessive_bools)]
#[derive(Clone, Debug)]
pub struct Constructs {
/// Attention.
///
/// ```markdown
/// > | a *b* c **d**.
/// ^^^ ^^^^^
/// ```
pub attention: bool,
/// Autolink.
///
/// ```markdown
/// > | a <https://example.com> b <user@example.org>.
/// ^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^
/// ```
pub autolink: bool,
/// Block quote.
///
/// ```markdown
/// > | > a
/// ^^^
/// ```
pub block_quote: bool,
/// Character escape.
///
/// ```markdown
/// > | a \* b
/// ^^
/// ```
pub character_escape: bool,
/// Character reference.
///
/// ```markdown
/// > | a & b
/// ^^^^^
/// ```
pub character_reference: bool,
/// Code (indented).
///
/// ```markdown
/// > | a
/// ^^^^^
/// ```
pub code_indented: bool,
/// Code (fenced).
///
/// ```markdown
/// > | ~~~js
/// ^^^^^
/// > | console.log(1)
/// ^^^^^^^^^^^^^^
/// > | ~~~
/// ^^^
/// ```
pub code_fenced: bool,
/// Code (text).
///
/// ```markdown
/// > | a `b` c
/// ^^^
/// ```
pub code_text: bool,
/// Definition.
///
/// ```markdown
/// > | [a]: b "c"
/// ^^^^^^^^^^
/// ```
pub definition: bool,
/// Hard break (escape).
///
/// ```markdown
/// > | a\
/// ^
/// | b
/// ```
pub hard_break_escape: bool,
/// Hard break (trailing).
///
/// ```markdown
/// > | a␠␠
/// ^^
/// | b
/// ```
pub hard_break_trailing: bool,
/// Heading (atx).
///
/// ```markdown
/// > | # a
/// ^^^
/// ```
pub heading_atx: bool,
/// Heading (setext).
///
/// ```markdown
/// > | a
/// ^^
/// > | ==
/// ^^
/// ```
pub heading_setext: bool,
/// HTML (flow).
///
/// ```markdown
/// > | <div>
/// ^^^^^
/// ```
pub html_flow: bool,
/// HTML (text).
///
/// ```markdown
/// > | a <b> c
/// ^^^
/// ```
pub html_text: bool,
/// Label start (image).
///
/// ```markdown
/// > | a ![b](c) d
/// ^^
/// ```
pub label_start_image: bool,
/// Label start (link).
///
/// ```markdown
/// > | a [b](c) d
/// ^
/// ```
pub label_start_link: bool,
/// Label end.
///
/// ```markdown
/// > | a [b](c) d
/// ^^^^
/// ```
pub label_end: bool,
/// List.
///
/// ```markdown
/// > | * a
/// ^^^
/// ```
pub list: bool,
/// Thematic break.
///
/// ```markdown
/// > | ***
/// ^^^
/// ```
pub thematic_break: bool,
}
impl Default for Constructs {
/// `CommonMark`.
fn default() -> Self {
Self {
attention: true,
autolink: true,
block_quote: true,
character_escape: true,
character_reference: true,
code_indented: true,
code_fenced: true,
code_text: true,
definition: true,
hard_break_escape: true,
hard_break_trailing: true,
heading_atx: true,
heading_setext: true,
html_flow: true,
html_text: true,
label_start_image: true,
label_start_link: true,
label_end: true,
list: true,
thematic_break: true,
}
}
}
/// Configuration (optional).
#[derive(Clone, Debug, Default)]
pub struct Options {
/// Whether to allow (dangerous) HTML.
/// The default is `false`, you can turn it on to `true` for trusted
/// content.
///
/// ## Examples
///
/// ```
/// use micromark::{micromark, micromark_with_options, Options};
///
/// // micromark is safe by default:
/// assert_eq!(
/// micromark("Hi, <i>venus</i>!"),
/// "<p>Hi, <i>venus</i>!</p>"
/// );
///
/// // Turn `allow_dangerous_html` on to allow potentially dangerous HTML:
/// assert_eq!(
/// micromark_with_options(
/// "Hi, <i>venus</i>!",
/// &Options {
/// allow_dangerous_html: true,
/// ..Options::default()
/// }
/// ),
/// "<p>Hi, <i>venus</i>!</p>"
/// );
/// ```
pub allow_dangerous_html: bool,
/// Whether to allow (dangerous) protocols in links and images.
/// The default is `false`, you can turn it on to `true` for trusted
/// content.
///
/// ## Examples
///
/// ```
/// use micromark::{micromark, micromark_with_options, Options};
///
/// // micromark is safe by default:
/// assert_eq!(
/// micromark("<javascript:alert(1)>"),
/// "<p><a href=\"\">javascript:alert(1)</a></p>"
/// );
///
/// // Turn `allow_dangerous_protocol` on to allow potentially dangerous protocols:
/// assert_eq!(
/// micromark_with_options(
/// "<javascript:alert(1)>",
/// &Options {
/// allow_dangerous_protocol: true,
/// ..Options::default()
/// }
/// ),
/// "<p><a href=\"javascript:alert(1)\">javascript:alert(1)</a></p>"
/// );
/// ```
pub allow_dangerous_protocol: bool,
/// Default line ending to use, for line endings not in `value`.
///
/// Generally, micromark copies line endings (`\r`, `\n`, `\r\n`) in the
/// markdown document over to the compiled HTML.
/// In some cases, such as `> a`, CommonMark requires that extra line
/// endings are added: `<blockquote>\n<p>a</p>\n</blockquote>`.
///
/// To create that line ending, the document is checked for the first line
/// ending that is used.
/// If there is no line ending, `default_line_ending` is used.
/// If that isn’t configured, `\n` is used.
///
/// ## Examples
///
/// ```
/// use micromark::{micromark, micromark_with_options, Options, LineEnding};
///
/// // micromark uses `\n` by default:
/// assert_eq!(
/// micromark("> a"),
/// "<blockquote>\n<p>a</p>\n</blockquote>"
/// );
///
/// // Define `default_line_ending` to configure the default:
/// assert_eq!(
/// micromark_with_options(
/// "> a",
/// &Options {
/// default_line_ending: LineEnding::CarriageReturnLineFeed,
/// ..Options::default()
/// }
/// ),
/// "<blockquote>\r\n<p>a</p>\r\n</blockquote>"
/// );
/// ```
pub default_line_ending: LineEnding,
/// Which constructs to enable and disable.
/// The default is to follow `CommonMark`.
///
/// ## Examples
///
/// ```
/// use micromark::{micromark, micromark_with_options, Options, Constructs};
///
/// // micromark follows CommonMark by default:
/// assert_eq!(
/// micromark(" indented code?"),
/// "<pre><code>indented code?\n</code></pre>"
/// );
///
/// // Pass `constructs` to choose what to enable and disable:
/// assert_eq!(
/// micromark_with_options(
/// " indented code?",
/// &Options {
/// constructs: Constructs {
/// code_indented: false,
/// ..Constructs::default()
/// },
/// ..Options::default()
/// }
/// ),
/// "<p>indented code?</p>"
/// );
/// ```
pub constructs: Constructs,
}
/// Turn markdown into HTML.
///
/// ## Examples
///
/// ```
/// use micromark::micromark;
///
/// let result = micromark("# Hello, world!");
///
/// assert_eq!(result, "<h1>Hello, world!</h1>");
/// ```
#[must_use]
pub fn micromark(value: &str) -> String {
micromark_with_options(value, &Options::default())
}
/// Turn markdown into HTML, with configuration.
///
/// ## Examples
///
/// ```
/// use micromark::{micromark_with_options, Options};
///
/// let result = micromark_with_options("<div>\n\n# Hello, world!\n\n</div>", &Options {
/// allow_dangerous_html: true,
/// allow_dangerous_protocol: true,
/// ..Options::default()
/// });
///
/// assert_eq!(result, "<div>\n<h1>Hello, world!</h1>\n</div>");
/// ```
#[must_use]
pub fn micromark_with_options(value: &str, options: &Options) -> String {
let (events, result) = parse(value, options);
compile(&events, &result.chars, options)
}