/// ^^^^^
/// ```
pub html_flow: bool,
/// HTML (text).
///
/// ```markdown
/// > | a
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 items.
///
/// ```markdown
/// > | * a
/// ^^^
/// ```
pub list_item: bool,
/// Math (flow).
///
/// ```markdown
/// > | $$
/// ^^
/// > | \frac{1}{2}
/// ^^^^^^^^^^^
/// > | $$
/// ^^
/// ```
pub math_flow: bool,
/// Math (text).
///
/// ```markdown
/// > | a $b$ c
/// ^^^
/// ```
pub math_text: bool,
/// MDX: ESM.
///
/// ```markdown
/// > | import a from 'b'
/// ^^^^^^^^^^^^^^^^^
/// ```
///
/// > 👉 **Note**: to support ESM, you *must* pass
/// > [`mdx_esm_parse`][MdxEsmParse] in [`ParseOptions`][] too.
/// > Otherwise, ESM is treated as normal markdown.
pub mdx_esm: bool,
/// MDX: expression (flow).
///
/// ```markdown
/// > | {Math.PI}
/// ^^^^^^^^^
/// ```
///
/// > 👉 **Note**: You *can* pass
/// > [`mdx_expression_parse`][MdxExpressionParse] in [`ParseOptions`][]
/// > too, to parse expressions according to a certain grammar (typically,
/// > a programming language).
/// > Otherwise, expressions are parsed with a basic algorithm that only
/// > cares about braces.
pub mdx_expression_flow: bool,
/// MDX: expression (text).
///
/// ```markdown
/// > | a {Math.PI} c
/// ^^^^^^^^^
/// ```
///
/// > 👉 **Note**: You *can* pass
/// > [`mdx_expression_parse`][MdxExpressionParse] in [`ParseOptions`][]
/// > too, to parse expressions according to a certain grammar (typically,
/// > a programming language).
/// > Otherwise, expressions are parsed with a basic algorithm that only
/// > cares about braces.
pub mdx_expression_text: bool,
/// MDX: JSX (flow).
///
/// ```markdown
/// > |
/// ^^^^^^^^^^^^^
/// ```
///
/// > 👉 **Note**: You *must* pass `html_flow: false` to use this,
/// > as it’s preferred when on over `mdx_jsx_flow`.
///
/// > 👉 **Note**: You *can* pass
/// > [`mdx_expression_parse`][MdxExpressionParse] in [`ParseOptions`][]
/// > too, to parse expressions in JSX according to a certain grammar
/// > (typically, a programming language).
/// > Otherwise, expressions are parsed with a basic algorithm that only
/// > cares about braces.
pub mdx_jsx_flow: bool,
/// MDX: JSX (text).
///
/// ```markdown
/// > | a c
/// ^^^^^^^^^^^^^
/// ```
///
/// > 👉 **Note**: You *must* pass `html_text: false` to use this,
/// > as it’s preferred when on over `mdx_jsx_text`.
///
/// > 👉 **Note**: You *can* pass
/// > [`mdx_expression_parse`][MdxExpressionParse] in [`ParseOptions`][]
/// > too, to parse expressions in JSX according to a certain grammar
/// > (typically, a programming language).
/// > Otherwise, expressions are parsed with a basic algorithm that only
/// > cares about braces.
pub mdx_jsx_text: bool,
/// Thematic break.
///
/// ```markdown
/// > | ***
/// ^^^
/// ```
pub thematic_break: bool,
}
impl Default for Constructs {
/// `CommonMark`.
///
/// `CommonMark` is a relatively strong specification of how markdown
/// works.
/// Most markdown parsers try to follow it.
///
/// For more information, see the `CommonMark` specification:
/// .
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,
frontmatter: false,
gfm_autolink_literal: false,
gfm_label_start_footnote: false,
gfm_footnote_definition: false,
gfm_strikethrough: false,
gfm_table: false,
gfm_task_list_item: false,
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_item: true,
math_flow: false,
math_text: false,
mdx_esm: false,
mdx_expression_flow: false,
mdx_expression_text: false,
mdx_jsx_flow: false,
mdx_jsx_text: false,
thematic_break: true,
}
}
}
impl Constructs {
/// GFM.
///
/// GFM stands for **GitHub flavored markdown**.
/// GFM extends `CommonMark` and adds support for autolink literals,
/// footnotes, strikethrough, tables, and tasklists.
///
/// For more information, see the GFM specification:
/// .
#[must_use]
pub fn gfm() -> Self {
Self {
gfm_autolink_literal: true,
gfm_footnote_definition: true,
gfm_label_start_footnote: true,
gfm_strikethrough: true,
gfm_table: true,
gfm_task_list_item: true,
..Self::default()
}
}
/// MDX.
///
/// This turns on `CommonMark`, turns off some conflicting constructs
/// (autolinks, code (indented), and HTML), and turns on MDX (ESM,
/// expressions, and JSX).
///
/// For more information, see the MDX website:
/// .
///
/// > 👉 **Note**: to support ESM, you *must* pass
/// > [`mdx_esm_parse`][MdxEsmParse] in [`ParseOptions`][] too.
/// > Otherwise, ESM is treated as normal markdown.
/// >
/// > You *can* pass
/// > [`mdx_expression_parse`][MdxExpressionParse]
/// > to parse expressions according to a certain grammar (typically, a
/// > programming language).
/// > Otherwise, expressions are parsed with a basic algorithm that only
/// > cares about braces.
#[must_use]
pub fn mdx() -> Self {
Self {
autolink: false,
code_indented: false,
html_flow: false,
html_text: false,
mdx_esm: true,
mdx_expression_flow: true,
mdx_expression_text: true,
mdx_jsx_flow: true,
mdx_jsx_text: true,
..Self::default()
}
}
}
/// Configuration that describes how to compile to HTML.
///
/// You likely either want to turn on the dangerous options
/// (`allow_dangerous_html`, `allow_dangerous_protocol`) when dealing with
/// input you trust, or want to customize how GFM footnotes are compiled
/// (typically because the input markdown is not in English).
///
/// ## Examples
///
/// ```
/// use markdown::CompileOptions;
/// # fn main() {
///
/// // Use the default trait to get safe defaults:
/// let safe = CompileOptions::default();
///
/// // Live dangerously / trust the author:
/// let danger = CompileOptions {
/// allow_dangerous_html: true,
/// allow_dangerous_protocol: true,
/// ..CompileOptions::default()
/// };
///
/// // In French:
/// let enFrançais = CompileOptions {
/// gfm_footnote_label: Some("Notes de bas de page".into()),
/// gfm_footnote_back_label: Some("Arrière".into()),
/// ..CompileOptions::default()
/// };
/// # }
/// ```
#[allow(clippy::struct_excessive_bools)]
#[derive(Clone, Debug, Default)]
pub struct CompileOptions {
/// Whether to allow (dangerous) HTML.
///
/// The default is `false`, which still parses the HTML according to
/// `CommonMark` but shows the HTML as text instead of as elements.
///
/// Pass `true` for trusted content to get actual HTML elements.
///
/// ## Examples
///
/// ```
/// use markdown::{to_html, to_html_with_options, CompileOptions, Options};
/// # fn main() -> Result<(), String> {
///
/// // `markdown-rs` is safe by default:
/// assert_eq!(
/// to_html("Hi, venus!"),
/// "Hi, <i>venus</i>!
"
/// );
///
/// // Turn `allow_dangerous_html` on to allow potentially dangerous HTML:
/// assert_eq!(
/// to_html_with_options(
/// "Hi, venus!",
/// &Options {
/// compile: CompileOptions {
/// allow_dangerous_html: true,
/// ..CompileOptions::default()
/// },
/// ..Options::default()
/// }
/// )?,
/// "Hi, venus!
"
/// );
/// # Ok(())
/// # }
/// ```
pub allow_dangerous_html: bool,
/// Whether to allow dangerous protocols in links and images.
///
/// The default is `false`, which drops URLs in links and images that use
/// dangerous protocols.
///
/// Pass `true` for trusted content to support all protocols.
///
/// URLs that have no protocol (which means it’s relative to the current
/// page, such as `./some/page.html`) and URLs that have a safe protocol
/// (for images: `http`, `https`; for links: `http`, `https`, `irc`,
/// `ircs`, `mailto`, `xmpp`), are safe.
/// All other URLs are dangerous and dropped.
///
/// ## Examples
///
/// ```
/// use markdown::{to_html, to_html_with_options, CompileOptions, Options};
/// # fn main() -> Result<(), String> {
///
/// // `markdown-rs` is safe by default:
/// assert_eq!(
/// to_html(""),
/// "javascript:alert(1)
"
/// );
///
/// // Turn `allow_dangerous_protocol` on to allow potentially dangerous protocols:
/// assert_eq!(
/// to_html_with_options(
/// "",
/// &Options {
/// compile: CompileOptions {
/// allow_dangerous_protocol: true,
/// ..CompileOptions::default()
/// },
/// ..Options::default()
/// }
/// )?,
/// "javascript:alert(1)
"
/// );
/// # Ok(())
/// # }
/// ```
pub allow_dangerous_protocol: bool,
/// Default line ending to use when compiling to HTML, for line endings not
/// in `value`.
///
/// Generally, `markdown-rs` 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: `\na
\n
`.
///
/// 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 markdown::{to_html, to_html_with_options, CompileOptions, LineEnding, Options};
/// # fn main() -> Result<(), String> {
///
/// // `markdown-rs` uses `\n` by default:
/// assert_eq!(
/// to_html("> a"),
/// "\na
\n
"
/// );
///
/// // Define `default_line_ending` to configure the default:
/// assert_eq!(
/// to_html_with_options(
/// "> a",
/// &Options {
/// compile: CompileOptions {
/// default_line_ending: LineEnding::CarriageReturnLineFeed,
/// ..CompileOptions::default()
/// },
/// ..Options::default()
/// }
/// )?,
/// "\r\na
\r\n
"
/// );
/// # Ok(())
/// # }
/// ```
pub default_line_ending: LineEnding,
/// Textual label to use for the footnotes section.
///
/// The default value is `"Footnotes"`.
/// Change it when the markdown is not in English.
///
/// This label is typically hidden visually (assuming a `sr-only` CSS class
/// is defined that does that), and thus affects screen readers only.
/// If you do have such a class, but want to show this section to everyone,
/// pass different attributes with the `gfm_footnote_label_attributes`
/// option.
///
/// ## Examples
///
/// ```
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
/// # fn main() -> Result<(), String> {
///
/// // `"Footnotes"` is used by default:
/// assert_eq!(
/// to_html_with_options(
/// "[^a]\n\n[^a]: b",
/// &Options::gfm()
/// )?,
/// "1
\n\n"
/// );
///
/// // Pass `gfm_footnote_label` to use something else:
/// assert_eq!(
/// to_html_with_options(
/// "[^a]\n\n[^a]: b",
/// &Options {
/// parse: ParseOptions::gfm(),
/// compile: CompileOptions {
/// gfm_footnote_label: Some("Notes de bas de page".into()),
/// ..CompileOptions::gfm()
/// }
/// }
/// )?,
/// "1
\n\n"
/// );
/// # Ok(())
/// # }
/// ```
pub gfm_footnote_label: Option,
/// HTML tag name to use for the footnote label element.
///
/// The default value is `"h2"`.
/// Change it to match your document structure.
///
/// This label is typically hidden visually (assuming a `sr-only` CSS class
/// is defined that does that), and thus affects screen readers only.
/// If you do have such a class, but want to show this section to everyone,
/// pass different attributes with the `gfm_footnote_label_attributes`
/// option.
///
/// ## Examples
///
/// ```
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
/// # fn main() -> Result<(), String> {
///
/// // `"h2"` is used by default:
/// assert_eq!(
/// to_html_with_options(
/// "[^a]\n\n[^a]: b",
/// &Options::gfm()
/// )?,
/// "1
\n\n"
/// );
///
/// // Pass `gfm_footnote_label_tag_name` to use something else:
/// assert_eq!(
/// to_html_with_options(
/// "[^a]\n\n[^a]: b",
/// &Options {
/// parse: ParseOptions::gfm(),
/// compile: CompileOptions {
/// gfm_footnote_label_tag_name: Some("h1".into()),
/// ..CompileOptions::gfm()
/// }
/// }
/// )?,
/// "1
\n\n"
/// );
/// # Ok(())
/// # }
/// ```
pub gfm_footnote_label_tag_name: Option,
/// Attributes to use on the footnote label.
///
/// The default value is `"class=\"sr-only\""`.
/// Change it to show the label and add other attributes.
///
/// This label is typically hidden visually (assuming a `sr-only` CSS class
/// is defined that does that), and thus affects screen readers only.
/// If you do have such a class, but want to show this section to everyone,
/// pass an empty string.
/// You can also add different attributes.
///
/// > 👉 **Note**: `id="footnote-label"` is always added, because footnote
/// > calls use it with `aria-describedby` to provide an accessible label.
///
/// ## Examples
///
/// ```
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
/// # fn main() -> Result<(), String> {
///
/// // `"class=\"sr-only\""` is used by default:
/// assert_eq!(
/// to_html_with_options(
/// "[^a]\n\n[^a]: b",
/// &Options::gfm()
/// )?,
/// "1
\n\n"
/// );
///
/// // Pass `gfm_footnote_label_attributes` to use something else:
/// assert_eq!(
/// to_html_with_options(
/// "[^a]\n\n[^a]: b",
/// &Options {
/// parse: ParseOptions::gfm(),
/// compile: CompileOptions {
/// gfm_footnote_label_attributes: Some("class=\"footnote-heading\"".into()),
/// ..CompileOptions::gfm()
/// }
/// }
/// )?,
/// "1
\n\n"
/// );
/// # Ok(())
/// # }
/// ```
pub gfm_footnote_label_attributes: Option,
/// Textual label to describe the backreference back to footnote calls.
///
/// The default value is `"Back to content"`.
/// Change it when the markdown is not in English.
///
/// This label is used in the `aria-label` attribute on each backreference
/// (the `↩` links).
/// It affects users of assistive technology.
///
/// ## Examples
///
/// ```
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
/// # fn main() -> Result<(), String> {
///
/// // `"Back to content"` is used by default:
/// assert_eq!(
/// to_html_with_options(
/// "[^a]\n\n[^a]: b",
/// &Options::gfm()
/// )?,
/// "1
\n\n"
/// );
///
/// // Pass `gfm_footnote_back_label` to use something else:
/// assert_eq!(
/// to_html_with_options(
/// "[^a]\n\n[^a]: b",
/// &Options {
/// parse: ParseOptions::gfm(),
/// compile: CompileOptions {
/// gfm_footnote_back_label: Some("Arrière".into()),
/// ..CompileOptions::gfm()
/// }
/// }
/// )?,
/// "1
\n\n"
/// );
/// # Ok(())
/// # }
/// ```
pub gfm_footnote_back_label: Option,
/// Prefix to use before the `id` attribute on footnotes to prevent them
/// from *clobbering*.
///
/// The default is `"user-content-"`.
/// Pass `Some("".into())` for trusted markdown and when you are careful
/// with polyfilling.
/// You could pass a different prefix.
///
/// DOM clobbering is this:
///
/// ```html
///
///
/// ```
///
/// The above example shows that elements are made available by browsers,
/// by their ID, on the `window` object.
/// This is a security risk because you might be expecting some other
/// variable at that place.
/// It can also break polyfills.
/// Using a prefix solves these problems.
///
/// ## Examples
///
/// ```
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
/// # fn main() -> Result<(), String> {
///
/// // `"user-content-"` is used by default:
/// assert_eq!(
/// to_html_with_options(
/// "[^a]\n\n[^a]: b",
/// &Options::gfm()
/// )?,
/// "1
\n\n"
/// );
///
/// // Pass `gfm_footnote_clobber_prefix` to use something else:
/// assert_eq!(
/// to_html_with_options(
/// "[^a]\n\n[^a]: b",
/// &Options {
/// parse: ParseOptions::gfm(),
/// compile: CompileOptions {
/// gfm_footnote_clobber_prefix: Some("".into()),
/// ..CompileOptions::gfm()
/// }
/// }
/// )?,
/// "1
\n\n"
/// );
/// # Ok(())
/// # }
/// ```
pub gfm_footnote_clobber_prefix: Option,
/// Whether to support the GFM tagfilter.
///
/// This option does nothing if `allow_dangerous_html` is not turned on.
/// The default is `false`, which does not apply the GFM tagfilter to HTML.
/// Pass `true` for output that is a bit closer to GitHub’s actual output.
///
/// The tagfilter is kinda weird and kinda useless.
/// The tag filter is a naïve attempt at XSS protection.
/// You should use a proper HTML sanitizing algorithm instead.
///
/// ## Examples
///
/// ```
/// use markdown::{to_html_with_options, CompileOptions, Options, ParseOptions};
/// # fn main() -> Result<(), String> {
///
/// // With `allow_dangerous_html`, `markdown-rs` passes HTML through untouched:
/// assert_eq!(
/// to_html_with_options(
/// "