comrak 0.59.0

A 100% CommonMark-compatible GitHub Flavored Markdown parser and formatter
Documentation
use pretty_assertions::assert_eq;
use std::cell::Cell;

use super::*;

fn parse_stopping_on_call<'a>(
    arena: &'a Arena<'a>,
    input: &str,
    options: &Options,
    stop_at: usize,
) -> (Result<Node<'a>, ParseCancelled>, usize) {
    let calls = Cell::new(0);
    let result = try_parse_document(arena, input, options, &|| {
        calls.set(calls.get() + 1);
        calls.get() >= stop_at
    });
    (result, calls.get())
}

fn mixed_input() -> String {
    [
        include_str!("fixtures/alerts.md"),
        include_str!("fixtures/description_lists.md"),
        include_str!("fixtures/math_code.md"),
        include_str!("fixtures/math_dollars.md"),
        include_str!("fixtures/multiline_alerts.md"),
        include_str!("fixtures/multiline_blockquote.md"),
        include_str!("fixtures/wikilinks_title_after_pipe.md"),
        include_str!("fixtures/wikilinks_title_before_pipe.md"),
        "| a | b |\n|---|---|\n| [ ] c | d@e.com |\n\n",
        "- [x] www.example.com[^1] ^[inline *note*]\n  - é **ü** \"smart\" --- ok\n\n",
        "[^1]: The *footnote*.\n\n[ref]: /url 'title'\n\n[ref] and [missing]\n",
    ]
    .concat()
}

fn many_options() -> Options<'static> {
    let mut options = Options::default();
    options.extension.strikethrough = true;
    options.extension.table = true;
    options.extension.autolink = true;
    options.extension.tasklist = true;
    options.extension.superscript = true;
    options.extension.header_id_prefix = Some(String::new());
    options.extension.footnotes = true;
    options.extension.inline_footnotes = true;
    options.extension.description_lists = true;
    options.extension.multiline_block_quotes = true;
    options.extension.alerts = true;
    options.extension.math_dollars = true;
    options.extension.math_code = true;
    options.extension.wikilinks_title_after_pipe = true;
    options.extension.underline = true;
    options.extension.spoiler = true;
    options.parse.smart = true;
    options.parse.relaxed_autolinks = true;
    options
}

#[test]
fn stops_block_parsing() {
    let input = "a\n\n".repeat(10_000);
    let arena = Arena::new();
    let (result, calls) = parse_stopping_on_call(&arena, &input, &Options::default(), 2);
    assert_eq!(result.unwrap_err(), ParseCancelled);
    assert_eq!(calls, 2);
    assert!(arena.len() < 200, "made {} nodes", arena.len());
}

#[test]
fn stops_inline_parsing() {
    let input = "a *b* ".repeat(20_000);
    let arena = Arena::new();
    let (result, calls) = parse_stopping_on_call(&arena, &input, &Options::default(), 2);
    assert_eq!(result.unwrap_err(), ParseCancelled);
    assert_eq!(calls, 2);
    assert!(arena.len() < 1_000, "made {} nodes", arena.len());
}

#[test]
fn stops_on_every_call() {
    let input = mixed_input();

    for sourcepos_chars in [false, true] {
        let mut options = many_options();
        options.parse.sourcepos_chars = sourcepos_chars;

        let arena = Arena::new();
        let (result, total) = parse_stopping_on_call(&arena, &input, &options, usize::MAX);
        result.unwrap();

        for stop_at in 1..=total {
            let arena = Arena::new();
            let (result, calls) = parse_stopping_on_call(&arena, &input, &options, stop_at);
            assert_eq!(
                result.unwrap_err(),
                ParseCancelled,
                "stopping on call {stop_at} of {total}"
            );
            assert_eq!(calls, stop_at);
        }

        let arena = Arena::new();
        let (result, calls) = parse_stopping_on_call(&arena, &input, &options, total + 1);
        result.unwrap();
        assert_eq!(calls, total);
    }
}

#[test]
fn output_unchanged_when_never_stopped() {
    let input = mixed_input();
    let mut options = many_options();
    options.render.sourcepos = true;

    let arena = Arena::new();
    let expected = parse_document(&arena, &input, &options);
    let actual = try_parse_document(&arena, &input, &options, &|| false).unwrap();

    let mut expected_xml = String::new();
    let mut actual_xml = String::new();
    format_xml(expected, &options, &mut expected_xml).unwrap();
    format_xml(actual, &options, &mut actual_xml).unwrap();
    assert_eq!(actual_xml, expected_xml);
}