#![allow(dead_code)]
use crate::parser::Link;
use crate::take_until_unbalanced;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::*;
use nom::error::ErrorKind;
use std::borrow::Cow;
const ESCAPABLE: &str = r#"\'"()[]{}<>"#;
pub fn md_link(i: &str) -> nom::IResult<&str, Link> {
let (i, link_text) = md_link_text(i)?;
let (i, (link_destination, link_title)) = md_link_destination_enclosed(i)?;
Ok((i, Link::Inline(link_text, link_destination, link_title)))
}
pub fn md_link_ref_def(i: &str) -> nom::IResult<&str, Link> {
let (i, _) = nom::bytes::complete::take_while_m_n(0, 3, |c| c == ' ')(i)?;
let (i, link_text) = md_link_label(i)?;
let (i, _) = nom::character::complete::char(':')(i)?;
let (i, _) = verify(nom::character::complete::multispace1, |s: &str| {
s.find("\n\n").is_none()
})(i)?;
let (i, link_destination) = md_link_destination(i)?;
if let Ok((i, _)) = verify(
nom::character::complete::multispace1::<_, (_, ErrorKind)>,
|s: &str| s.find("\n\n").is_none(),
)(i)
{
let (i, link_title) = verify(md_link_title, |s: &str| s.find("\n\n").is_none())(i)?;
Ok((i, Link::RefDef(link_text, link_destination, link_title)))
} else {
Ok((
i,
Link::RefDef(link_text, link_destination, Cow::Borrowed("")),
))
}
}
pub fn md_ref(i: &str) -> nom::IResult<&str, Link> {
let (i, (link_text, link_label)) = alt((
nom::sequence::pair(md_link_text, md_link_label),
nom::combinator::map(nom::sequence::terminated(md_link_text, tag("[]")), |s| {
(s.clone(), s)
}),
nom::combinator::map(md_link_text, |s| (s.clone(), s)),
))(i)?;
Ok((i, Link::Ref(link_text, link_label)))
}
fn md_link_text(i: &str) -> nom::IResult<&str, Cow<str>> {
nom::combinator::map_parser(
nom::sequence::delimited(tag("["), take_until_unbalanced('[', ']'), tag("]")),
md_escaped_str_transform,
)(i)
}
fn md_link_label(i: &str) -> nom::IResult<&str, Cow<str>> {
nom::combinator::map_parser(
nom::sequence::delimited(
tag("["),
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\[]"#),
'\\',
nom::character::complete::one_of(ESCAPABLE),
),
tag("]"),
),
md_escaped_str_transform,
)(i)
}
fn md_link_destination(i: &str) -> nom::IResult<&str, Cow<str>> {
nom::combinator::map_parser(md_parse_link_destination, md_escaped_str_transform)(i)
}
fn md_parse_link_destination(i: &str) -> nom::IResult<&str, &str> {
alt((
nom::sequence::delimited(
tag("<"),
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\<>"#),
'\\',
nom::character::complete::one_of(ESCAPABLE),
),
tag(">"),
),
map_parser(
nom::bytes::complete::is_not(" \t\r\n"),
all_consuming(take_until_unbalanced('(', ')')),
),
))(i)
}
fn md_link_destination_enclosed(i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>)> {
let (rest, inner) =
nom::sequence::delimited(tag("("), take_until_unbalanced('(', ')'), tag(")"))(i)?;
let (i, link_destination) = md_link_destination(inner)?;
if let Ok((i, _)) = nom::character::complete::multispace1::<_, (_, ErrorKind)>(i) {
let (_, link_title) = md_link_title(i)?;
Ok((rest, (link_destination, link_title)))
} else {
Ok((rest, (link_destination, Cow::from(""))))
}
}
fn md_link_title(i: &str) -> nom::IResult<&str, Cow<str>> {
nom::combinator::map_parser(md_parse_link_title, md_escaped_str_transform)(i)
}
fn md_parse_link_title(i: &str) -> nom::IResult<&str, &str> {
verify(
alt((
nom::sequence::delimited(tag("("), take_until_unbalanced('(', ')'), tag(")")),
nom::sequence::delimited(
tag("'"),
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\'"#),
'\\',
nom::character::complete::one_of(ESCAPABLE),
),
tag("'"),
),
nom::sequence::delimited(
tag("\""),
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\""#),
'\\',
nom::character::complete::one_of(ESCAPABLE),
),
tag("\""),
),
)),
|s: &str| s.find("\n\n").is_none(),
)(i)
}
fn md_escaped_str_transform(i: &str) -> nom::IResult<&str, Cow<str>> {
nom::combinator::map(
nom::bytes::complete::escaped_transform(
nom::bytes::complete::is_not("\\"),
'\\',
nom::character::complete::one_of(ESCAPABLE),
),
|s| if s == i { Cow::from(i) } else { Cow::from(s) },
)(i)
}
#[cfg(test)]
mod tests {
use super::*;
use nom::error::ErrorKind;
#[test]
fn test_md_link() {
assert_eq!(
md_link("[text](url)abc"),
Ok((
"abc",
Link::Inline(Cow::from("text"), Cow::from("url"), Cow::from(""))
))
);
assert_eq!(
md_link("[text[i]](url)abc"),
Ok((
"abc",
Link::Inline(Cow::from("text[i]"), Cow::from("url"), Cow::from(""))
))
);
assert_eq!(
md_link("[text[i]](ur(l))abc"),
Ok((
"abc",
Link::Inline(Cow::from("text[i]"), Cow::from("ur(l)"), Cow::from(""))
))
);
assert_eq!(
md_link("[text(url)"),
Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
);
assert_eq!(
md_link("[text](<url>)abc"),
Ok((
"abc",
Link::Inline(Cow::from("text"), Cow::from("url"), Cow::from(""))
))
);
assert_eq!(
md_link("[text](<url> \"link title\")abc"),
Ok((
"abc",
Link::Inline(Cow::from("text"), Cow::from("url"), Cow::from("link title"))
))
);
assert_eq!(
md_link("[text](url \"link title\")abc"),
Ok((
"abc",
Link::Inline(Cow::from("text"), Cow::from("url"), Cow::from("link title"))
))
);
}
#[test]
fn test_md_link_ref_def() {
assert_eq!(
md_link_ref_def("[text]: url\n\"abc\""),
Ok((
"",
Link::RefDef(Cow::from("text"), Cow::from("url"), Cow::from("abc"))
))
);
assert_eq!(
md_link_ref_def(" [text]: url\n\"abc\""),
Ok((
"",
Link::RefDef(Cow::from("text"), Cow::from("url"), Cow::from("abc"))
))
);
assert_eq!(
md_link_ref_def("abc[text]: url\n\"abc\""),
Err(nom::Err::Error(nom::error::Error::new(
"abc[text]: url\n\"abc\"",
ErrorKind::Tag
)))
);
assert_eq!(
md_link_ref_def(" [text]: url\n\"abc\""),
Err(nom::Err::Error(nom::error::Error::new(
" [text]: url\n\"abc\"",
ErrorKind::Tag
)))
);
assert_eq!(
md_link_ref_def(r#"[text\[i\]]: ur(l)url"#),
Ok((
"",
Link::RefDef(Cow::from("text[i]"), Cow::from("ur(l)url"), Cow::from(""))
))
);
assert_eq!(
md_link_ref_def("[text[i]]: ur(l)(url"),
Err(nom::Err::Error(nom::error::Error::new(
"[i]]: ur(l)(url",
ErrorKind::Tag
)))
);
assert_eq!(
md_link_ref_def("[text]: \nurl"),
Ok((
"",
Link::RefDef(Cow::from("text"), Cow::from("url"), Cow::from(""))
))
);
assert_eq!(
md_link_ref_def("[text]: \n\nurl"),
Err(nom::Err::Error(nom::error::Error::new(
" \n\nurl",
ErrorKind::Verify
)))
);
assert_eq!(
md_link_ref_def("[text: url"),
Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
);
assert_eq!(
md_link_ref_def("[text] url"),
Err(nom::Err::Error(nom::error::Error::new(
" url",
ErrorKind::Char
)))
);
assert_eq!(
md_link_ref_def("[text]: url \"link title\"\nabc"),
Ok((
"\nabc",
Link::RefDef(Cow::from("text"), Cow::from("url"), Cow::from("link title"))
))
);
assert_eq!(
md_link_ref_def("[text]: url \"link\ntitle\"\nabc"),
Ok((
"\nabc",
Link::RefDef(
Cow::from("text"),
Cow::from("url"),
Cow::from("link\ntitle")
)
))
);
assert_eq!(
md_link_ref_def("[text]: url \"link\n\ntitle\"\nabc"),
Err(nom::Err::Error(nom::error::Error::new(
"\"link\n\ntitle\"\nabc",
ErrorKind::Verify
)))
);
assert_eq!(
md_link_ref_def("[text]:\nurl \"link\ntitle\"\nabc"),
Ok((
"\nabc",
Link::RefDef(
Cow::from("text"),
Cow::from("url"),
Cow::from("link\ntitle")
)
))
);
assert_eq!(
md_link_ref_def("[text]:\n\nurl \"link title\"\nabc"),
Err(nom::Err::Error(nom::error::Error::new(
"\n\nurl \"link title\"\nabc",
ErrorKind::Verify
)))
);
}
#[test]
fn test_md_link_text() {
assert_eq!(
md_link_text("[text](url)"),
Ok(("(url)", Cow::from("text")))
);
assert_eq!(
md_link_text("[text[i]](url)"),
Ok(("(url)", Cow::from("text[i]")))
);
assert_eq!(
md_link_text(r#"[text\[i\]](url)"#),
Ok(("(url)", Cow::from("text[i]")))
);
assert_eq!(
md_link_text("[text(url)"),
Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
);
}
#[test]
fn test_md_link_label() {
assert_eq!(
md_link_label("[text]: url"),
Ok((": url", Cow::from("text")))
);
assert_eq!(
md_link_label(r#"[text\[i\]]: url"#),
Ok((": url", Cow::from("text[i]")))
);
assert_eq!(
md_link_label("[text: url"),
Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
);
assert_eq!(
md_link_label("[t[ext: url"),
Err(nom::Err::Error(nom::error::Error::new(
"[ext: url",
ErrorKind::Tag
)))
);
}
#[test]
fn test_md_link_destination() {
assert_eq!(
md_link_destination("<url>abc"),
Ok(("abc", Cow::from("url")))
);
assert_eq!(
md_link_destination(r#"<u\<r\>l>abc"#),
Ok(("abc", Cow::from(r#"u<r>l"#)))
);
}
#[test]
fn test_md_parse_link_destination() {
assert_eq!(md_parse_link_destination("<url>abc"), Ok(("abc", "url")));
assert_eq!(
md_parse_link_destination(r#"<u\<r\>l>abc"#),
Ok(("abc", r#"u\<r\>l"#))
);
assert_eq!(
md_parse_link_destination("<url 2>abc"),
Ok(("abc", "url 2"))
);
assert_eq!(md_parse_link_destination("url abc"), Ok((" abc", "url")));
assert_eq!(
md_parse_link_destination("<url(1)> abc"),
Ok((" abc", "url(1)"))
);
assert_eq!(
md_parse_link_destination(r#"<[1a]\[1b\](2a)\(2b\)\<3b\>{4a}\{4b\}> abc"#),
Ok((" abc", r#"[1a]\[1b\](2a)\(2b\)\<3b\>{4a}\{4b\}"#))
);
assert_eq!(
md_parse_link_destination("ur()l abc"),
Ok((" abc", "ur()l"))
);
}
#[test]
fn test_md_escaped_str_transform() {
assert_eq!(md_escaped_str_transform(""), Ok(("", Cow::from(""))));
assert_eq!(md_escaped_str_transform(" "), Ok(("", Cow::from(" "))));
assert_eq!(
md_escaped_str_transform(r#"abc`:<>abc"#),
Ok(("", Cow::from(r#"abc`:<>abc"#)))
);
assert_eq!(
md_escaped_str_transform(r#"\<\>\\"#),
Ok(("", Cow::from(r#"<>\"#)))
);
assert_eq!(
md_escaped_str_transform(r#"\(\)\\"#),
Ok(("", Cow::from(r#"()\"#)))
);
}
#[test]
fn test_md_link_title() {
assert_eq!(md_link_title("(title)abc"), Ok(("abc", Cow::from("title"))));
assert_eq!(
md_link_title("(ti(t)le)abc"),
Ok(("abc", Cow::from("ti(t)le")))
);
assert_eq!(
md_link_title(r#""1\\23\"4\'56"abc"#),
Ok(("abc", Cow::from(r#"1\23"4'56"#)))
);
assert_eq!(
md_link_title("\"tu\nvwxy\"abc"),
Ok(("abc", Cow::from("tu\nvwxy")))
);
assert_eq!(
md_link_title("'tu\nv\\\'wxy'abc"),
Ok(("abc", Cow::from("tu\nv\'wxy")))
);
assert_eq!(
md_link_title("(ti\n\ntle)abc"),
Err(nom::Err::Error(nom::error::Error::new(
"(ti\n\ntle)abc",
ErrorKind::Verify
)))
);
}
#[test]
fn test_md_parse_link_title() {
assert_eq!(md_parse_link_title("(title)abc"), Ok(("abc", "title")));
assert_eq!(md_parse_link_title("(ti(t)le)abc"), Ok(("abc", "ti(t)le")));
assert_eq!(
md_parse_link_title(r#""1\\23\"4\'56"abc"#),
Ok(("abc", r#"1\\23\"4\'56"#))
);
assert_eq!(
md_parse_link_title("\"tu\nvwxy\"abc"),
Ok(("abc", "tu\nvwxy"))
);
assert_eq!(
md_parse_link_title("'tu\nv\\\'wxy'abc"),
Ok(("abc", "tu\nv\\\'wxy"))
);
assert_eq!(
md_parse_link_title("(ti\n\ntle)abc"),
Err(nom::Err::Error(nom::error::Error::new(
"(ti\n\ntle)abc",
ErrorKind::Verify
)))
);
}
}