\n```\n```\n\n```",
"```\n
\n```\n```\n\n```",
),
];
const LINES: &[Case] = &[
(
"a carriage return ends a line",
"```\r\r```\rtext\r",
"```\r\r```\rtext\r````````",
),
(
"U+2424 ends a line",
"```\u{2424}\u{2424}```",
"```\u{2424}\u{2424}```",
),
(
"CRLF is one line end, not an empty line",
"`a\r\n``",
"`a\r\n` ```` `",
),
(
"a line of spaces inside an alt is written as it is",
"",
"``",
),
(
"an alt split by CRLF keeps it",
"",
"``",
),
];
const SPANS: &[Case] = &[
(
"a span on one line is code",
"a\n``",
"a\n``",
),
(
"after a lone backtick, a span of its length is prose",
"`a\n``",
"`a\n` ```` `",
),
(
"a span of another length is still code",
"`a\n````",
"`a\n````",
),
(
"a span holding a run of that length is prose",
"`lone\n``a `  ``",
"`lone\n``a ` `````` ``",
),
(
"an empty line starts afresh",
"`a\n\n``",
"`a\n\n``",
),
(
"a line of spaces does not",
"`a\n \n``",
"`a\n \n` ```` `",
),
(
"a span that does not close on its line is prose",
"`a\nb` ",
"`a\nb` ````",
),
(
"a span holding a pipe is prose (a table cell ends there)",
"| `x |  | y` |",
"| `x | ```` | y` |",
),
(
"an escaped backtick opens nothing",
"\\``",
"\\` ```` `",
),
(
"a backtick in a URL opens nothing (marked reads it as the URL's)",
"https://x.example/a`b  `",
"https://x.example/a`b ```` `",
),
(
"a backtick in a link destination opens nothing",
"[a](x `)  `",
"[a](x `) ```` `",
),
];
const BRACKETS: &[Case] = &[
(
"a label holding a bracket in code",
"![a `]` b](https://e.example/a)",
"``![a ``\\` ``]``\\` `` b](https://e.example/a)``",
),
(
"a label holding a lone backtick",
" `",
"`````` `",
),
(
"nested brackets and escapes in the label",
"![a [b] \\] c](https://e.example/a)",
"`![a [b] \\] c](https://e.example/a)`",
),
(
"nested parentheses in the destination; a backslash does not escape one",
"\\)c) d",
"`\\)`c) d",
),
(
"no parenthesis closes the destination",
",
];
/// What marked lets take a backtick before a code span starts: emphasis, a tag, a URL, a link.
const STEALS: &[Case] = &[
(
"emphasis before a span that holds its closer makes it prose",
"** `**  `",
"** `** ```` `",
),
(
"a tag before a span that holds a `>` makes it prose",
"  `",
" ```` `",
),
(
"a `[` that starts no link is escaped when a `](` follows",
"[x [y](https://l.example) and [z]",
"\\[x [y](https://l.example) and [z]",
),
(
"the characters of a clean link count as seen",
"[a*b](https://l.example) `*`",
"[a*b](https://l.example) `* ```` `",
),
(
"inline LaTeX before a span that holds a `$` makes it prose",
"$a `$  `",
"$a `$ ```` `",
),
(
"a new span splits at a `$` seen before it, which marked does not escape",
"$x ",
"$x ``",
),
(
"but not at a `$` seen only after it",
" $x",
"`` $x",
),
(
"a new span splits at a closer seen before it",
"*a* ",
"*a* ``",
),
(
"but not at one seen only after it",
" *a*",
"`` *a*",
),
(
"marked may read part of a run into a clean URL",
"www.x.example/p```)``",
"www.x.example/p```)` ```````` `",
),
(
"a clean URL ends at a `<` before a line end, which may start an autolink",
"e.org/p<\ne.org/p",
"e.org/p<\ne.org/p ``",
),
(
"a clean URL holding an image goes in code whole",
"https://x.example/ end",
"`https://x.example/` end",
),
];
fn check(table: &[Case]) {
for &(case, input, clean) in table {
let once = disarm(input, false);
assert_eq!(once.text, clean, "{case}");
assert_eq!(disarm(clean, false).text, clean, "{case}: twice");
}
}
#[test]
fn fences_both_parsers_read_as_code() {
check(FENCES);
}
#[test]
fn lines_end_where_marked_ends_them() {
check(LINES);
}
#[test]
fn spans_both_parsers_read_as_code() {
check(SPANS);
}
#[test]
fn labels_and_destinations_end_where_either_parser_ends_them() {
check(BRACKETS);
}
#[test]
fn what_marked_lets_take_a_backtick_leaves_no_image() {
check(STEALS);
}
#[test]
fn a_construct_is_looked_for_in_its_first_2048_characters() {
// From its `[` to its `)`: 2048 characters, found whole.
let image = |alt: usize| format!("", "a".repeat(alt));
assert_eq!(
disarm(&image(2025), false).text,
format!("`{}`", image(2025))
);
// One more, and the `)` is out of reach: the image is read as `![alt]`.
let alt = "a".repeat(2026);
let got = disarm(&image(2026), false).text;
assert_eq!(got, format!("`![{alt}]`(https://e.example/x)"));
// With the `]` out of reach too, only the `"));
let got = disarm(&image(2047), true).text;
assert_eq!(got, format!("`"));
// A link out of reach keeps only its URL in code.
let got = disarm(&format!("[{alt}](https://l.example/x)"), true);
assert_eq!(got.text, format!("[{alt}]( `https://l.example/x`)"));
assert!(got.links);
}
#[test]
fn an_untrusted_reply_follows_the_same_rules() {
let text = "- item\n```\nhttps://e.example/a\n```\n\n```\nhttps://e.example/b\n```";
let got = disarm(text, true);
assert_eq!(
got.text,
"- item\n```\n````https://e.example/a````\n```\n\n```\nhttps://e.example/b\n```"
);
assert!(got.links);
}