#![allow(dead_code)]
use crate::parser::Link;
use nom::branch::alt;
use nom::bytes::complete::tag;
use nom::combinator::*;
use nom::IResult;
use std::borrow::Cow;
const ESCAPABLE: &str = r#" `:<>_\"#;
pub fn rst_text2dest_link(i: &str) -> nom::IResult<&str, Link> {
let (i, (te, de, ti)) = rst_text2dest(i)?;
Ok((i, Link::Text2Dest(te, de, ti)))
}
pub fn rst_text2dest(i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>, Cow<str>)> {
let (i, (ln, ld)) = rst_parse_text2target(true, false)(i)?;
let ln = rst_escaped_link_text_transform(ln)?.1;
let ld = rst_escaped_link_destination_transform(ld)?.1;
Ok((i, (ln, ld, Cow::Borrowed(""))))
}
pub fn rst_text_label2dest_link(i: &str) -> nom::IResult<&str, Link> {
let (i, (te, de, ti)) = rst_text_label2dest(i)?;
Ok((i, Link::TextLabel2Dest(te, de, ti)))
}
pub fn rst_text_label2dest(i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>, Cow<str>)> {
let (i, (ln, ld)) = rst_parse_text2target(false, false)(i)?;
let ln = rst_escaped_link_text_transform(ln)?.1;
let ld = rst_escaped_link_destination_transform(ld)?.1;
Ok((i, (ln, ld, Cow::Borrowed(""))))
}
fn rst_parse_text2target(
anonym: bool,
label: bool,
) -> impl Fn(&str) -> IResult<&str, (&str, &str)> {
move |i: &str| {
let (mut i, inner) = nom::sequence::delimited(
tag("`"),
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\`"#),
'\\',
nom::character::complete::one_of(ESCAPABLE),
),
tag("`_"),
)(i)?;
if anonym {
let (j, _) = nom::character::complete::char('_')(i)?;
i = j;
};
if i != "" {
let _ = nom::combinator::not(nom::character::complete::char('_'))(i)?;
};
let (inner_rest, link_text): (&str, &str) = nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\<"#),
'\\',
nom::character::complete::one_of(ESCAPABLE),
)(inner)?;
let link_text = link_text.trim_end();
let (j, mut link_dest_label) = nom::sequence::delimited(
tag("<"),
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\<>"#),
'\\',
nom::character::complete::one_of(ESCAPABLE),
),
tag(">"),
)(inner_rest)?;
let (_, _) = nom::combinator::eof(j)?;
let (_, _) = nom::combinator::not(nom::combinator::eof)(link_dest_label)?;
let last_char_is_ = link_dest_label.is_char_boundary(link_dest_label.len() - 1)
&& &link_dest_label[link_dest_label.len() - 1..] == "_";
if (label && !last_char_is_) || (!label && last_char_is_) {
return Err(nom::Err::Error(nom::error::Error::new(
i,
nom::error::ErrorKind::Tag,
)));
};
if label {
link_dest_label = &link_dest_label[..link_dest_label.len() - 1];
};
Ok((i, (link_text, link_dest_label)))
}
}
pub fn rst_text2label_link(i: &str) -> nom::IResult<&str, Link> {
let (i, (te, la)) = rst_text2label(i)?;
Ok((i, Link::Text2Label(te, la)))
}
pub fn rst_text2label(i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>)> {
let (i, (te, la)) = rst_parse_text2label(i)?;
let te = rst_escaped_link_text_transform(te)?.1;
let la = rst_escaped_link_text_transform(la)?.1;
Ok((i, (te, la)))
}
fn rst_parse_text2label(i: &str) -> nom::IResult<&str, (&str, &str)> {
fn take_word_consume_first_ending_underscore(i: &str) -> nom::IResult<&str, &str> {
let mut i = i;
let (k, mut r) =
nom::bytes::complete::take_till1(|c| c == ' ' || c == '\t' || c == '\n')(i)?;
if r.len() >= 2 && r.is_char_boundary(r.len() - 2) && &r[r.len() - 2..] == "__" {
i = &i[r.len() - 1..];
r = &r[..r.len() - 2];
} else if r.len() >= 1 && r.is_char_boundary(r.len() - 1) && &r[r.len() - 1..] == "_" {
i = k;
r = &r[..r.len() - 1]
} else {
return Err(nom::Err::Error(nom::error::Error::new(
k,
nom::error::ErrorKind::Tag,
)));
};
Ok((i, r))
}
let (mut i, (link_text, mut link_label)) = alt((
rst_parse_text2target(false, true),
nom::combinator::map(
nom::sequence::delimited(
tag("`"),
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\`"#),
'\\',
nom::character::complete::one_of(ESCAPABLE),
),
tag("`_"),
),
|s| (s, s),
),
nom::combinator::map(take_word_consume_first_ending_underscore, |s| (s, s)),
))(i)?;
if let (j, Some(_)) = nom::combinator::opt(nom::character::complete::char('_'))(i)? {
link_label = "_";
i = j;
};
Ok((i, (link_text, link_label)))
}
pub fn rst_label2dest_link(i: &str) -> nom::IResult<&str, Link> {
let (i, (l, d, t)) = rst_label2dest(i)?;
Ok((i, Link::Label2Dest(l, d, t)))
}
pub fn rst_label2dest(i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>, Cow<str>)> {
let my_err = |_| {
nom::Err::Error(nom::error::Error::new(
i,
nom::error::ErrorKind::EscapedTransform,
))
};
let (i, c) = rst_explicit_markup_block(i)?;
let (ln, ld) = match c {
Cow::Borrowed(s) => {
let (_, (ln, ld)) = rst_parse_label2dest(s)?;
(
rst_escaped_link_text_transform(ln)?.1,
rst_escaped_link_destination_transform(ld)?.1,
)
}
Cow::Owned(strg) => {
let (_, (ln, ld)) = rst_parse_label2dest(&strg).map_err(my_err)?;
let ln = Cow::Owned(
rst_escaped_link_text_transform(ln)
.map_err(my_err)?
.1
.to_string(),
);
let ld = Cow::Owned(
rst_escaped_link_destination_transform(ld)
.map_err(my_err)?
.1
.to_string(),
);
(ln, ld)
}
};
Ok((i, (ln, ld, Cow::Borrowed(""))))
}
fn rst_parse_label2dest(i: &str) -> nom::IResult<&str, (&str, &str)> {
let (i, _) = nom::character::complete::char('_')(i)?;
let (link_destination, link_text) = alt((
nom::sequence::delimited(
tag("`"),
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\`"#),
'\\',
nom::character::complete::one_of(r#" `:<>"#),
),
tag("`: "),
),
nom::sequence::terminated(
nom::bytes::complete::escaped(
nom::character::complete::none_of(r#"\:"#),
'\\',
nom::character::complete::one_of(r#" `:<>"#),
),
tag(": "),
),
))(i)?;
Ok(("", (link_text, link_destination)))
}
pub fn rst_label2label_link(i: &str) -> nom::IResult<&str, Link> {
let (i, (l1, l2)) = rst_label2label(i)?;
Ok((i, Link::Label2Label(l1, l2)))
}
pub fn rst_label2label(_i: &str) -> nom::IResult<&str, (Cow<str>, Cow<str>)> {
Ok(("", (Cow::from(""), Cow::from(""))))
}
fn rst_parse_label2label(_i: &str) -> nom::IResult<&str, (&str, &str)> {
Ok(("", ("", "")))
}
fn rst_explicit_markup_block(i: &str) -> nom::IResult<&str, Cow<str>> {
fn indent<'a>(wsp1: &'a str, wsp2: &'a str) -> impl Fn(&'a str) -> IResult<&'a str, ()> {
move |i: &str| {
let (i, _) = nom::character::complete::line_ending(i)?;
let (i, _) = nom::bytes::complete::tag(wsp1)(i)?;
let (i, _) = nom::bytes::complete::tag(wsp2)(i)?;
Ok((i, ()))
}
}
let (i, (wsp1, wsp2)) = nom::sequence::pair(
nom::character::complete::space0,
nom::combinator::map(nom::bytes::complete::tag(".. "), |_| " "),
)(i)?;
let (j, v) = nom::multi::separated_list1(
indent(&wsp1, &wsp2),
nom::character::complete::not_line_ending,
)(i)?;
if v.len() == 1 {
return Ok((j, Cow::Borrowed(v[0].clone())));
};
let mut s = String::new();
let mut is_first = true;
for subs in &v {
if !is_first {
s.push(' ');
}
s.push_str(subs);
is_first = false;
}
Ok((j, Cow::from(s)))
}
fn rst_escaped_link_text_transform(i: &str) -> IResult<&str, Cow<str>> {
nom::combinator::map(
nom::bytes::complete::escaped_transform(
nom::bytes::complete::is_not("\\"),
'\\',
alt((
tag("\\"),
tag("`"),
tag(":"),
tag("<"),
tag(">"),
tag("_"),
value("", tag(" ")),
)),
),
|s| if s == i { Cow::from(i) } else { Cow::from(s) },
)(i)
}
fn remove_whitespace(i: &str) -> IResult<&str, Cow<str>> {
let mut res = Cow::Borrowed("");
let mut j = i;
while j != "" {
let (k, _) = nom::character::complete::multispace0(j)?;
let (k, s) = nom::bytes::complete::escaped(
nom::character::complete::none_of("\\\r\n \t"),
'\\',
nom::character::complete::one_of(r#" :`<>\"#),
)(k)?;
res = match res {
Cow::Borrowed("") => Cow::Borrowed(s),
Cow::Borrowed(res_str) => {
let mut strg = res_str.to_string();
strg.push_str(s);
Cow::Owned(strg)
}
Cow::Owned(mut strg) => {
strg.push_str(s);
Cow::Owned(strg)
}
};
j = k;
}
Ok((j, res))
}
fn rst_escaped_link_destination_transform(i: &str) -> IResult<&str, Cow<str>> {
let my_err = |_| {
nom::Err::Error(nom::error::Error::new(
i,
nom::error::ErrorKind::EscapedTransform,
))
};
let c = &*remove_whitespace(i)?.1;
let s = nom::bytes::complete::escaped_transform::<_, nom::error::Error<_>, _, _, _, _, _, _>(
nom::bytes::complete::is_not("\\"),
'\\',
nom::character::complete::one_of(ESCAPABLE),
)(c)
.map_err(my_err)?
.1;
if s == i {
Ok(("", Cow::Borrowed(i)))
} else {
Ok(("", Cow::Owned(s.to_owned())))
}
}
#[cfg(test)]
mod tests {
use super::*;
use nom::error::ErrorKind;
#[test]
fn test_rst_text2dest() {
let expected = (
"abc",
(
Cow::from("Python home page"),
Cow::from("http://www.python.org"),
Cow::from(""),
),
);
assert_eq!(
rst_text2dest("`Python home page <http://www.python.org>`__abc").unwrap(),
expected
);
let expected = (
"abc",
(
Cow::from(r#"Python<home> page"#),
Cow::from("http://www.python.org"),
Cow::from(""),
),
);
assert_eq!(
rst_text2dest(r#"`Python\ \<home\> page <http://www.python.org>`__abc"#).unwrap(),
expected
);
let expected = (
"abc",
(
Cow::from(r#"my news at <http://python.org>"#),
Cow::from("http://news.python.org"),
Cow::from(""),
),
);
assert_eq!(
rst_text2dest(r#"`my news at \<http://python.org\> <http://news.python.org>`__abc"#)
.unwrap(),
expected
);
let expected = (
"abc",
(
Cow::from(r#"my news at <http://python.org>"#),
Cow::from(r#"http://news. <python>.org"#),
Cow::from(""),
),
);
assert_eq!(
rst_text2dest(
r#"`my news at \<http\://python.org\> <http:// news.\ \<python\>.org>`__abc"#
)
.unwrap(),
expected
);
}
#[test]
fn test_rst_parse_text2dest_label() {
let expected = ("abc", ("Python home page", "http://www.python.org"));
assert_eq!(
rst_parse_text2target(false, false)("`Python home page <http://www.python.org>`_abc")
.unwrap(),
expected
);
let expected = nom::Err::Error(nom::error::Error::new("abc", ErrorKind::Tag));
assert_eq!(
rst_parse_text2target(false, false)("`Python home page <http://www.python.org_>`_abc")
.unwrap_err(),
expected
);
let expected = nom::Err::Error(nom::error::Error::new("", ErrorKind::Tag));
assert_eq!(
rst_parse_text2target(false, false)("`_abc").unwrap_err(),
expected
);
let expected = ("abc", ("Python home page", "http://www.python.org"));
assert_eq!(
rst_parse_text2target(true, false)("`Python home page <http://www.python.org>`__abc")
.unwrap(),
expected
);
let expected = ("abc", (r#"Python\ \<home\> page"#, "http://www.python.org"));
assert_eq!(
rst_parse_text2target(false, false)(
r#"`Python\ \<home\> page <http://www.python.org>`_abc"#
)
.unwrap(),
expected
);
let expected = (
"abc",
(
r#"my news at \<http://python.org\>"#,
"http://news.python.org",
),
);
assert_eq!(
rst_parse_text2target(false, false)(
r#"`my news at \<http://python.org\> <http://news.python.org>`_abc"#
)
.unwrap(),
expected
);
let expected = (
"abc",
(
r#"my news at \<http\://python.org\>"#,
r#"http:// news.\ \<python\>.org"#,
),
);
assert_eq!(
rst_parse_text2target(false, false)(
r#"`my news at \<http\://python.org\> <http:// news.\ \<python\>.org>`_abc"#
)
.unwrap(),
expected
);
let expected = (
"abc",
(
r#"my news at \<http\://python.org\>"#,
r#"http:// news.\ \<python\>.org"#,
),
);
assert_eq!(
rst_parse_text2target(false, false)(
r#"`my news at \<http\://python.org\> <http:// news.\ \<python\>.org>`_abc"#
)
.unwrap(),
expected
);
let expected = ("abc", (r#"rst link text"#, "rst_link_label"));
assert_eq!(
rst_parse_text2target(false, true)(r#"`rst link text <rst_link_label_>`_abc"#).unwrap(),
expected
);
let expected = nom::Err::Error(nom::error::Error::new("abc", ErrorKind::Tag));
assert_eq!(
rst_parse_text2target(false, true)(r#"`my news <python webpage>`_abc"#).unwrap_err(),
expected
);
}
#[test]
fn test_rst_text2label() {
assert_eq!(
rst_text2label(r#"li\<nktext_ abc"#),
Ok((" abc", (Cow::from("li<nktext"), Cow::from("li<nktext"))))
);
assert_eq!(
rst_text2label(r#"`li\:nk text`_ abc"#),
Ok((" abc", (Cow::from("li:nk text"), Cow::from("li:nk text"))))
);
assert_eq!(
rst_text2label("`link text`__ abc"),
Ok((" abc", (Cow::from("link text"), Cow::from("_"))))
);
}
#[test]
fn test_rst_parse_text2label() {
assert_eq!(
rst_parse_text2label("linktext_ abc"),
Ok((" abc", ("linktext", "linktext")))
);
assert_eq!(
rst_parse_text2label("linktext__ abc"),
Ok((" abc", ("linktext", "_")))
);
assert_eq!(
rst_parse_text2label("link_text_ abc"),
Ok((" abc", ("link_text", "link_text")))
);
assert_eq!(
rst_parse_text2label("`link text`_ abc"),
Ok((" abc", ("link text", "link text")))
);
assert_eq!(
rst_parse_text2label("`link text`_abc"),
Ok(("abc", ("link text", "link text")))
);
assert_eq!(
rst_parse_text2label("`link_text`_ abc"),
Ok((" abc", ("link_text", "link_text")))
);
assert_eq!(
rst_parse_text2label("`link text`__ abc"),
Ok((" abc", ("link text", "_")))
);
assert_eq!(
rst_parse_text2label("`link text<link label_>`_ abc"),
Ok((" abc", ("link text", "link label")))
);
}
#[test]
fn test_rst_label2dest() {
let expected = (
"\nabc",
(
Cow::from("Python: home page"),
Cow::from("http://www.python.org"),
Cow::from(""),
),
);
assert_eq!(
rst_label2dest(".. _`Python: home page`: http://www.python.org\nabc").unwrap(),
expected
);
assert_eq!(
rst_label2dest(" .. _`Python: home page`: http://www.py\n thon.org \nabc")
.unwrap(),
expected
);
let expected = nom::Err::Error(nom::error::Error::new(
"x .. _`Python: home page`: http://www.python.org\nabc",
ErrorKind::Tag,
));
assert_eq!(
rst_label2dest("x .. _`Python: home page`: http://www.python.org\nabc").unwrap_err(),
expected
);
let expected = (
"",
(
Cow::from("Python: `home page`"),
Cow::from("http://www.python .org"),
Cow::from(""),
),
);
assert_eq!(
rst_label2dest(r#".. _Python\: \`home page\`: http://www.python\ .org"#).unwrap(),
expected
);
assert_eq!(
rst_label2dest(r#".. _`Python: \`home page\``: http://www.python\ .org"#).unwrap(),
expected
);
let expected = (
"",
(
Cow::from("my news at <http://python.org>"),
Cow::from("http://news.python.org"),
Cow::from(""),
),
);
assert_eq!(
rst_label2dest(r#".. _`my news at <http://python.org>`: http://news.python.org"#)
.unwrap(),
expected
);
assert_eq!(
rst_label2dest(r#".. _`my news at \<http://python.org\>`: http://news.python.org"#)
.unwrap(),
expected
);
assert_eq!(
rst_label2dest(r#".. _my news at \<http\://python.org\>: http://news.python.org"#)
.unwrap(),
expected
);
let expected = (
"",
(
Cow::from("my news"),
Cow::from("http://news.<python>.org"),
Cow::from(""),
),
);
assert_eq!(
rst_label2dest(r#".. _my news: http://news.<python>.org"#).unwrap(),
expected
);
assert_eq!(
rst_label2dest(r#".. _my news: http://news.\<python\>.org"#).unwrap(),
expected
);
}
#[test]
fn test_rst_parse_link_label2dest() {
let expected = ("", ("Python home page", "http://www.python.org"));
assert_eq!(
rst_parse_label2dest("_Python home page: http://www.python.org").unwrap(),
expected
);
assert_eq!(
rst_parse_label2dest("_`Python home page`: http://www.python.org").unwrap(),
expected
);
let expected = ("", ("Python: home page", "http://www.python.org"));
assert_eq!(
rst_parse_label2dest("_`Python: home page`: http://www.python.org").unwrap(),
expected
);
let expected = ("", (r#"Python\: home page"#, "http://www.python.org"));
assert_eq!(
rst_parse_label2dest(r#"_Python\: home page: http://www.python.org"#).unwrap(),
expected
);
let expected = (
"",
("my news at <http://python.org>", "http://news.python.org"),
);
assert_eq!(
rst_parse_label2dest(r#"_`my news at <http://python.org>`: http://news.python.org"#)
.unwrap(),
expected
);
let expected = (
"",
(
r#"my news at \<http://python.org\>"#,
"http://news.python.org",
),
);
assert_eq!(
rst_parse_label2dest(r#"_`my news at \<http://python.org\>`: http://news.python.org"#)
.unwrap(),
expected
);
let expected = (
"",
(
r#"my news at \<http\://python.org\>"#,
"http://news.python.org",
),
);
assert_eq!(
rst_parse_label2dest(r#"_my news at \<http\://python.org\>: http://news.python.org"#)
.unwrap(),
expected
);
}
#[test]
fn test_rst_explicit_markup_block() {
assert_eq!(
rst_explicit_markup_block(".. 11111"),
Ok(("", Cow::from("11111")))
);
assert_eq!(
rst_explicit_markup_block(" .. 11111\nout"),
Ok(("\nout", Cow::from("11111")))
);
assert_eq!(
rst_explicit_markup_block(" .. 11111\n 222222\n 333333\nout"),
Ok(("\nout", Cow::from("11111 222222 333333")))
);
assert_eq!(
rst_explicit_markup_block(" .. first\n second\n 1indent\nout"),
Ok(("\nout", Cow::from("first second 1indent")))
);
assert_eq!(
rst_explicit_markup_block(" ..first"),
Err(nom::Err::Error(nom::error::Error::new(
"..first",
ErrorKind::Tag
)))
);
assert_eq!(
rst_explicit_markup_block("x .. first"),
Err(nom::Err::Error(nom::error::Error::new(
"x .. first",
ErrorKind::Tag
)))
);
}
#[test]
fn test_rst_escaped_link_text_transform() {
assert_eq!(rst_escaped_link_text_transform(""), Ok(("", Cow::from(""))));
assert_eq!(
rst_escaped_link_text_transform(" "),
Ok(("", Cow::from(" ")))
);
assert_eq!(
rst_escaped_link_text_transform(r#"\ \ \ "#),
Ok(("", Cow::from("")))
);
assert_eq!(
rst_escaped_link_text_transform(r#"abc`:<>abc"#),
Ok(("", Cow::from(r#"abc`:<>abc"#)))
);
assert_eq!(
rst_escaped_link_text_transform(r#"\:\`\<\>\\"#),
Ok(("", Cow::from(r#":`<>\"#)))
);
}
#[test]
fn test_rst_escaped_link_destination_transform() {
assert_eq!(
rst_escaped_link_destination_transform(""),
Ok(("", Cow::Borrowed("")))
);
assert_eq!(
rst_escaped_link_destination_transform(" "),
Ok(("", Cow::Borrowed("")))
);
assert_eq!(
rst_escaped_link_destination_transform(" x x"),
Ok(("", Cow::Owned("xx".to_string())))
);
assert_eq!(
rst_escaped_link_destination_transform(r#"\ \ \ "#),
Ok(("", Cow::Owned(" ".to_string())))
);
assert_eq!(
rst_escaped_link_destination_transform(r#"abc`:<>abc"#),
Ok(("", Cow::Borrowed(r#"abc`:<>abc"#)))
);
assert_eq!(
rst_escaped_link_destination_transform(r#"\:\`\<\>\\"#),
Ok(("", Cow::Owned(r#":`<>\"#.to_string())))
);
}
#[test]
fn test_remove_whitespace() {
assert_eq!(remove_whitespace(" abc "), Ok(("", Cow::Borrowed("abc"))));
assert_eq!(
remove_whitespace(" x x"),
Ok(("", Cow::Owned("xx".to_string())))
);
assert_eq!(remove_whitespace(" \t \r \n"), Ok(("", Cow::from(""))));
assert_eq!(
remove_whitespace(r#"\ \ \ "#),
Ok(("", Cow::Borrowed(r#"\ \ \ "#)))
);
assert_eq!(
remove_whitespace(r#"abc`:<>abc"#),
Ok(("", Cow::Borrowed(r#"abc`:<>abc"#)))
);
assert_eq!(
remove_whitespace(r#"\:\`\<\>\\"#),
Ok(("", Cow::Borrowed(r#"\:\`\<\>\\"#)))
);
assert_eq!(
remove_whitespace("http://www.py\n thon.org"),
Ok(("", Cow::Owned("http://www.python.org".to_string())))
);
}
}