#![allow(dead_code)]
use crate::take_until_unmatched;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::*;
use nom::error::ErrorKind;
pub fn md_link(i: &str) -> nom::IResult<&str, (&str, &str, &str)> {
let (i, link_text) = md_link_text(i)?;
let (i, (link_destination, link_title)) = md_link_destination_enclosed(i)?;
Ok((i, (link_text, link_destination, link_title)))
}
pub fn md_link_ref(i: &str) -> nom::IResult<&str, (&str, &str, &str)> {
let (i, _) = nom::bytes::complete::take_while_m_n(0, 3, |c| c == ' ')(i)?;
let (i, link_text) = md_link_ref_text(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_text, link_destination, link_title)))
} else {
Ok((i, (link_text, link_destination, "")))
}
}
fn md_link_text(i: &str) -> nom::IResult<&str, &str> {
nom::sequence::delimited(tag("["), take_until_unmatched('[', ']'), tag("]"))(i)
}
fn md_link_ref_text(i: &str) -> nom::IResult<&str, &str> {
nom::sequence::delimited(tag("["), take_until_unmatched('[', ']'), tag("]:"))(i)
}
fn md_link_destination(i: &str) -> nom::IResult<&str, &str> {
alt((
nom::sequence::delimited(tag("<"), take_until_unmatched('<', '>'), tag(">")),
map_parser(
nom::bytes::complete::is_not(" \t\r\n"),
all_consuming(take_until_unmatched('(', ')')),
),
))(i)
}
fn md_link_destination_enclosed(i: &str) -> nom::IResult<&str, (&str, &str)> {
let (rest, inner) =
nom::sequence::delimited(tag("("), take_until_unmatched('(', ')'), 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, "")))
}
}
fn md_link_title(i: &str) -> nom::IResult<&str, &str> {
verify(
alt((
nom::sequence::delimited(tag("("), take_until_unmatched('(', ')'), tag(")")),
nom::sequence::delimited(
tag("'"),
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\'"#),
'\\',
nom::character::complete::one_of(r#"n"'()"#),
),
tag("'"),
),
nom::sequence::delimited(
tag("\""),
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\""#),
'\\',
nom::character::complete::one_of(r#"n"'()"#),
),
tag("\""),
),
)),
|s: &str| s.find("\n\n").is_none(),
)(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", ("text", "url", ""))));
assert_eq!(
md_link("[text[i]](url)abc"),
Ok(("abc", ("text[i]", "url", "")))
);
assert_eq!(
md_link("[text[i]](ur(l))abc"),
Ok(("abc", ("text[i]", "ur(l)", "")))
);
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", ("text", "url", "")))
);
assert_eq!(
md_link("[text](<url> \"link title\")abc"),
Ok(("abc", ("text", "url", "link title")))
);
assert_eq!(
md_link("[text](url \"link title\")abc"),
Ok(("abc", ("text", "url", "link title")))
);
}
#[test]
fn test_md_link_ref() {
assert_eq!(
md_link_ref("[text]: url\n\"abc\""),
Ok(("", ("text", "url", "abc")))
);
assert_eq!(
md_link_ref(" [text]: url\n\"abc\""),
Ok(("", ("text", "url", "abc")))
);
assert_eq!(
md_link_ref("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(" [text]: url\n\"abc\""),
Err(nom::Err::Error(nom::error::Error::new(
" [text]: url\n\"abc\"",
ErrorKind::Tag
)))
);
assert_eq!(
md_link_ref("[text[i]]: ur(l)url"),
Ok(("", ("text[i]", "ur(l)url", "")))
);
assert_eq!(
md_link_ref("[text[i]]: ur(l)(url"),
Err(nom::Err::Error(nom::error::Error::new(
"ur(l)(url",
ErrorKind::TakeUntil
)))
);
assert_eq!(md_link_ref("[text]: \nurl"), Ok(("", ("text", "url", ""))));
assert_eq!(
md_link_ref("[text]: \n\nurl"),
Err(nom::Err::Error(nom::error::Error::new(
" \n\nurl",
ErrorKind::Verify
)))
);
assert_eq!(
md_link_ref("[text: url"),
Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
);
assert_eq!(
md_link_ref("[text] url"),
Err(nom::Err::Error(nom::error::Error::new(
"] url",
ErrorKind::Tag
)))
);
assert_eq!(
md_link_ref("[text]: url \"link title\"\nabc"),
Ok(("\nabc", ("text", "url", "link title")))
);
assert_eq!(
md_link_ref("[text]: url \"link\ntitle\"\nabc"),
Ok(("\nabc", ("text", "url", "link\ntitle")))
);
assert_eq!(
md_link_ref("[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("[text]:\nurl \"link\ntitle\"\nabc"),
Ok(("\nabc", ("text", "url", "link\ntitle")))
);
assert_eq!(
md_link_ref("[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)", "text")));
assert_eq!(md_link_text("[text[i]](url)"), Ok(("(url)", "text[i]")));
assert_eq!(
md_link_text("[text(url)"),
Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
);
}
#[test]
fn test_md_link_ref_text() {
assert_eq!(md_link_ref_text("[text]: url"), Ok((" url", "text")));
assert_eq!(md_link_ref_text("[text[i]]: url"), Ok((" url", "text[i]")));
assert_eq!(
md_link_ref_text("[text: url"),
Err(nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag)))
);
assert_eq!(
md_link_ref_text("[t[ext: url"),
Err(nom::Err::Error(nom::error::Error::new(
"t[ext: url",
ErrorKind::TakeUntil
)))
);
}
#[test]
fn test_md_link_destination() {
assert_eq!(md_link_destination("<url>abc"), Ok(("abc", "url")));
assert_eq!(md_link_destination("<url 2>abc"), Ok(("abc", "url 2")));
assert_eq!(md_link_destination("url abc"), Ok((" abc", "url")));
assert_eq!(md_link_destination("<url(1)> abc"), Ok((" abc", "url(1)")));
assert_eq!(md_link_destination("ur()l abc"), Ok((" abc", "ur()l")));
}
#[test]
fn test_md_link_title() {
assert_eq!(md_link_title("(title)abc"), Ok(("abc", "title")));
assert_eq!(md_link_title("(ti(t)le)abc"), Ok(("abc", "ti(t)le")));
assert_eq!(
md_link_title(r#""123\"456"abc"#),
Ok(("abc", r#"123\"456"#))
);
assert_eq!(
md_link_title(r#""tu\nv\"wxy"abc"#),
Ok(("abc", r#"tu\nv\"wxy"#))
);
assert_eq!(
md_link_title(r#"'tu\nv\'wxy'abc"#),
Ok(("abc", r#"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
)))
);
}
}