use nom::{branch::*, combinator::*, multi::*, IResult};
use crate::{constants::*, tui::misc_types::list_of::List, *};
#[rustfmt::skip]
pub fn parse_markdown(input: &str) -> IResult<&str, MdDocument> {
fn parse_tags_list(input: &str) -> IResult<&str, List<&str>>
{
parse_csv_opt_eol(TAGS, input)
}
fn parse_authors_list(input: &str) -> IResult<&str, List<&str>>
{
parse_csv_opt_eol(AUTHORS, input)
}
fn parse_title_value(input: &str) -> IResult<&str, &str>
{
parse_kv_opt_eol(TITLE, input)
}
fn parse_date_value(input: &str) -> IResult<&str, &str>
{
parse_kv_opt_eol(DATE, input)
}
let (input, output) = many0(
alt((
map(parse_title_value, MdBlockElement::Title),
map(parse_tags_list, MdBlockElement::Tags),
map(parse_authors_list, MdBlockElement::Authors),
map(parse_date_value, MdBlockElement::Date),
map(parse_block_heading_opt_eol, MdBlockElement::Heading),
map(parse_block_smart_list, MdBlockElement::SmartList),
map(parse_block_code, MdBlockElement::CodeBlock),
map(parse_block_markdown_text_until_eol, MdBlockElement::Text),
)),
)(input)?;
let it = List::from(output);
Ok((input, it))
}
#[cfg(test)]
mod tests {
use crossterm::style::Stylize;
use r3bl_rs_utils_core::*;
use super::*;
#[test]
fn test_parse_markdown_valid() {
let input = vec to install foobar.",
"```python",
"import foobar",
"",
"foobar.pluralize('word') # returns 'words'",
"foobar.pluralize('goose') # returns 'geese'",
"foobar.singularize('phenomena') # returns 'phenomenon'",
"```",
"- ul1",
"- ul2",
"1. ol1",
"2. ol2",
"- [ ] todo",
"- [x] done",
"end",
"",
]
.join("\n");
let (remainder, vec_block) = parse_markdown(&input).unwrap();
let expected_vec = vec![
MdBlockElement::Title("Something"),
MdBlockElement::Tags(list!["tag1", "tag2", "tag3"]),
MdBlockElement::Heading(HeadingData {
level: HeadingLevel::Heading1,
text: "Foobar",
}),
MdBlockElement::Text(list![]), MdBlockElement::Text(list![MdLineFragment::Plain(
"Foobar is a Python library for dealing with word pluralization.",
)]),
MdBlockElement::Text(list![]), MdBlockElement::CodeBlock(convert_into_code_block_lines(
Some("bash"),
vec!["pip install foobar"],
)),
MdBlockElement::CodeBlock(convert_into_code_block_lines(
Some("fish"),
vec![],
)),
MdBlockElement::CodeBlock(convert_into_code_block_lines(
Some("python"),
vec![""],
)),
MdBlockElement::Heading(HeadingData {
level: HeadingLevel::Heading2,
text: "Installation",
}),
MdBlockElement::Text(list![]), MdBlockElement::Text(list![
MdLineFragment::Plain("Use the package manager "),
MdLineFragment::Link(HyperlinkData::from((
"pip",
"https://pip.pypa.io/en/stable/",
))),
MdLineFragment::Plain(" to install foobar."),
]),
MdBlockElement::CodeBlock(convert_into_code_block_lines(
Some("python"),
vec![
"import foobar",
"",
"foobar.pluralize('word') # returns 'words'",
"foobar.pluralize('goose') # returns 'geese'",
"foobar.singularize('phenomena') # returns 'phenomenon'",
],
)),
MdBlockElement::SmartList((
list![list![
MdLineFragment::UnorderedListBullet {
indent: 0,
is_first_line: true
},
MdLineFragment::Plain("ul1"),
],],
BulletKind::Unordered,
0,
)),
MdBlockElement::SmartList((
list![list![
MdLineFragment::UnorderedListBullet {
indent: 0,
is_first_line: true
},
MdLineFragment::Plain("ul2"),
],],
BulletKind::Unordered,
0,
)),
MdBlockElement::SmartList((
list![list![
MdLineFragment::OrderedListBullet {
indent: 0,
number: 1,
is_first_line: true
},
MdLineFragment::Plain("ol1"),
],],
BulletKind::Ordered(1),
0,
)),
MdBlockElement::SmartList((
list![list![
MdLineFragment::OrderedListBullet {
indent: 0,
number: 2,
is_first_line: true
},
MdLineFragment::Plain("ol2"),
],],
BulletKind::Ordered(2),
0,
)),
MdBlockElement::SmartList((
list![list![
MdLineFragment::UnorderedListBullet {
indent: 0,
is_first_line: true
},
MdLineFragment::Checkbox(false),
MdLineFragment::Plain(" todo"),
],],
BulletKind::Unordered,
0,
)),
MdBlockElement::SmartList((
list![list![
MdLineFragment::UnorderedListBullet {
indent: 0,
is_first_line: true
},
MdLineFragment::Checkbox(true),
MdLineFragment::Plain(" done"),
],],
BulletKind::Unordered,
0,
)),
MdBlockElement::Text(list![MdLineFragment::Plain("end")]),
];
for block in vec_block.iter().skip(vec_block.len() - 7) {
println!(
"{0} {1}",
"█ → ".magenta().bold(),
format!("{:?}", block).green()
);
}
assert_eq2!(remainder, "");
vec_block
.iter()
.zip(expected_vec.iter())
.for_each(|(lhs, rhs)| assert_eq2!(lhs, rhs));
}
#[test]
fn test_markdown_invalid() {
let input = [
"@tags: [foo, bar",
"",
"```rs",
"let a=1;",
"```",
"",
"*italic* **bold** [link](https://example.com)",
"",
"`inline code`",
]
.join("\n");
let (remainder, blocks) = parse_markdown(&input).unwrap();
assert_eq2!(remainder, "`inline code`");
assert_eq2!(blocks.len(), 6);
}
}