use constants::*;
use nom::{branch::*, bytes::complete::*, combinator::*, sequence::*, IResult};
use crate::*;
#[rustfmt::skip]
pub fn parse_block_code(input: &str) -> IResult<&str, List<CodeBlockLine>> {
let (remainder, (lang, code)) = tuple((
parse_code_block_lang_to_eol,
parse_code_block_body_to_code_block_end,
))(input)?;
let (remainder, _) = opt(tag(NEW_LINE))(remainder)?;
let acc = split_by_newline(code);
return Ok((remainder, convert_into_code_block_lines(lang, acc)));
}
#[rustfmt::skip]
fn parse_code_block_lang_to_eol(input: &str) -> IResult<&str, Option<&str>> {
alt((
map(
preceded(
tag(CODE_BLOCK_START_PARTIAL),
terminated(
is_not(NEW_LINE),
tag(NEW_LINE),
),
),
Some,
),
map(
tuple((tag(CODE_BLOCK_START_PARTIAL), tag(NEW_LINE))),
|_| None,
),
))(input)
}
#[rustfmt::skip]
fn parse_code_block_body_to_code_block_end(input: &str) -> IResult<&str, &str> {
let (remainder, output) = terminated(
take_until(CODE_BLOCK_END),
tag(CODE_BLOCK_END),
)(input)?;
Ok((remainder, output))
}
pub fn split_by_newline(input: &str) -> Vec<&str> {
let mut acc: Vec<&str> = input.split('\n').collect();
if let Some(last_item) = acc.last() {
if last_item.is_empty() {
acc.pop();
}
}
acc
}
pub fn convert_into_code_block_lines<'input>(
lang: Option<&'input str>,
lines: Vec<&'input str>,
) -> List<CodeBlockLine<'input>> {
let mut acc = List::with_capacity(lines.len() + 2);
acc += CodeBlockLine {
language: lang,
content: CodeBlockLineContent::StartTag,
};
for line in lines {
acc += CodeBlockLine {
language: lang,
content: CodeBlockLineContent::Text(line),
};
}
acc += CodeBlockLine {
language: lang,
content: CodeBlockLineContent::EndTag,
};
acc
}
#[cfg(test)]
mod tests {
use r3bl_rs_utils_core::assert_eq2;
use super::*;
#[test]
fn test_parse_codeblock_trailing_extra() {
let input = "```bash\npip install foobar\n````";
let lang = "bash";
let code_lines = vec!["pip install foobar"];
let (remainder, code_block_lines) = parse_block_code(input).unwrap();
assert_eq2!(remainder, "`");
assert_eq2!(
code_block_lines,
convert_into_code_block_lines(Some(lang), code_lines)
);
}
#[test]
fn test_convert_from_code_block_into_lines() {
{
let language = Some("rust");
let lines = vec![];
let expected = list![
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::StartTag,
},
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::EndTag,
},
];
let output = convert_into_code_block_lines(language, lines);
assert_eq2!(output, expected);
}
{
let language = Some("rust");
let lines = vec![""];
let expected = list![
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::StartTag,
},
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::Text(""),
},
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::EndTag,
},
];
let output = convert_into_code_block_lines(language, lines);
assert_eq2!(output, expected);
}
{
let language = Some("rust");
let lines = vec!["let x = 1;"];
let expected = list![
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::StartTag,
},
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::Text("let x = 1;"),
},
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::EndTag,
},
];
let output = convert_into_code_block_lines(language, lines);
assert_eq2!(output, expected);
}
{
let language = Some("rust");
let lines = vec!["let x = 1;", "let y = 2;"];
let expected = list![
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::StartTag,
},
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::Text("let x = 1;"),
},
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::Text("let y = 2;"),
},
CodeBlockLine {
language: Some("rust"),
content: CodeBlockLineContent::EndTag,
},
];
let output = convert_into_code_block_lines(language, lines);
assert_eq2!(output, expected);
}
}
#[test]
fn test_parse_codeblock_split_by_eol() {
assert_eq2!(split_by_newline("foobar\n"), vec!["foobar"]);
assert_eq2!(split_by_newline("\n"), vec![""]);
assert_eq2!(split_by_newline(""), Vec::<&str>::new());
assert_eq2!(split_by_newline("foo\nbar\n"), vec!["foo", "bar"]);
assert_eq2!(split_by_newline("\nfoo\nbar\n"), vec!["", "foo", "bar"]);
}
#[test]
fn test_parse_codeblock() {
{
let lang = "bash";
let code_lines = vec!["pip install foobar"];
let input = ["```bash", "pip install foobar", "```", ""].join("\n");
println!("{:#?}", &input);
let (remainder, code_block_lines) = parse_block_code(&input).unwrap();
assert_eq2!(remainder, "");
assert_eq2!(
code_block_lines,
convert_into_code_block_lines(Some(lang), code_lines)
);
}
{
let lang = "bash";
let code_lines = vec![];
let input = ["```bash", "```", ""].join("\n");
let (remainder, code_block_lines) = parse_block_code(&input).unwrap();
assert_eq2!(remainder, "");
assert_eq2!(
code_block_lines,
convert_into_code_block_lines(Some(lang), code_lines)
);
}
{
let lang = "bash";
let code_lines = vec![""];
let input = ["```bash", "", "```", ""].join("\n");
let (remainder, code_block_lines) = parse_block_code(&input).unwrap();
assert_eq2!(remainder, "");
assert_eq2!(
code_block_lines,
convert_into_code_block_lines(Some(lang), code_lines)
);
}
{
let lang = "python";
let code_lines = vec![
"import foobar",
"",
"foobar.pluralize('word') # returns 'words'",
"foobar.pluralize('goose') # returns 'geese'",
"foobar.singularize('phenomena') # returns 'phenomenon'",
];
let input = [
"```python",
"import foobar",
"",
"foobar.pluralize('word') # returns 'words'",
"foobar.pluralize('goose') # returns 'geese'",
"foobar.singularize('phenomena') # returns 'phenomenon'",
"```",
"",
]
.join("\n");
let (remainder, code_block_lines) = parse_block_code(&input).unwrap();
assert_eq2!(remainder, "");
assert_eq2!(
code_block_lines,
convert_into_code_block_lines(Some(lang), code_lines)
);
}
}
#[test]
fn test_parse_codeblock_no_language() {
let lang = None;
let code_lines = vec!["pip install foobar"];
let input = ["```", "pip install foobar", "```", ""].join("\n");
let (remainder, code_block_lines) = parse_block_code(&input).unwrap();
assert_eq2!(remainder, "");
assert_eq2!(
code_block_lines,
convert_into_code_block_lines(lang, code_lines)
);
}
}