use constants::*;
use nom::{branch::*,
bytes::complete::*,
character::complete::*,
combinator::*,
multi::*,
sequence::*,
IResult};
use crate::*;
#[rustfmt::skip]
pub fn parse_csv_opt_eol<'a>(tag_name: &'a str, input: &'a str) -> IResult<&'a str, List<&'a str>> {
let (remainder, tags_text) = preceded(
tuple((tag(tag_name), tag(COLON), tag(SPACE))),
alt((
is_not(NEW_LINE),
recognize(many1(anychar)),
)),
)(input)?;
if tags_text.starts_with(NEW_LINE) {
if let Some(stripped) = tags_text.strip_prefix(NEW_LINE) {
return Ok((stripped, list![]));
}
}
let (_, vec_tags_text) = parse_tag_list_comma_separated(tags_text)?;
let (remainder, _) = opt(tag(NEW_LINE))(remainder)?;
return Ok((remainder, List::from(vec_tags_text)));
fn parse_tag_list_comma_separated(input: &str) -> IResult<&str, Vec<&str>> {
let acc: Vec<&str> = input.split(COMMA).collect();
let mut trimmed_acc: Vec<&str> = Vec::with_capacity(acc.len());
match acc.len() {
0 => {
}
1 => {
let only_item = &acc[0];
if only_item.starts_with(SPACE) {
return Err(nom::Err::Error(nom::error::Error::new(
"Only item must not start with space.",
nom::error::ErrorKind::Fail,
)));
}
else {
trimmed_acc.push(only_item);
}
},
_ => {
let mut my_iter = acc.iter();
let first_item = my_iter.next().unwrap();
if first_item.starts_with(SPACE) {
return Err(nom::Err::Error(nom::error::Error::new(
"First item must not start with space.",
nom::error::ErrorKind::Fail,
)));
}else {
trimmed_acc.push(first_item);
}
for rest_item in my_iter {
if !rest_item.starts_with(SPACE) {
return Err(nom::Err::Error(nom::error::Error::new(
"Non-first item must start with space.",
nom::error::ErrorKind::Fail,
)));
}
trimmed_acc.push(&rest_item[1..]);
}
},
}
Ok((input, trimmed_acc))
}
}
#[cfg(test)]
mod test_parse_tags_opt_eol {
use r3bl_rs_utils_core::assert_eq2;
use super::*;
#[test]
fn test_not_quoted_no_eol() {
let input = "@tags: tag1, tag2, tag3";
let (input, output) = super::parse_csv_opt_eol(TAGS, input).unwrap();
assert_eq2!(input, "");
assert_eq2!(output, list!["tag1", "tag2", "tag3"]);
}
#[test]
fn test_not_quoted_no_eol_err_whitespace() {
assert_eq2!(
parse_csv_opt_eol(TAGS, "@tags: tag1, tag2, tag3").is_err(),
true,
);
assert_eq2!(
parse_csv_opt_eol(TAGS, "@tags: tag1,tag2, tag3").is_err(),
true,
);
assert_eq2!(
parse_csv_opt_eol(TAGS, "@tags: tag1, tag2,tag3").is_err(),
true,
);
assert_eq2!(
parse_csv_opt_eol(TAGS, "@tags: tag1, tag2,tag3").is_err(),
true,
);
assert_eq2!(
parse_csv_opt_eol(TAGS, "@tags: tag1, tag2, tag3").unwrap(),
("", list!["tag1", "tag2", " tag3"]),
);
}
#[test]
fn test_not_quoted_with_eol() {
{
let input = "@tags: tag1, tag2, tag3\n";
let (input, output) = parse_csv_opt_eol(TAGS, input).unwrap();
assert_eq2!(input, "");
assert_eq2!(output, list!["tag1", "tag2", "tag3"]);
}
{
let input = "@tags: tag1, tag2, tag3\n]\n";
let result = parse_csv_opt_eol(TAGS, input);
assert_eq2!(result.is_err(), false);
}
{
let input = "@tags: tag1, tag2, tag3";
let result = parse_csv_opt_eol(TAGS, input);
assert_eq2!(result.is_err(), false);
}
}
#[test]
fn test_not_quoted_with_eol_whitespace() {
assert_eq2!(
parse_csv_opt_eol(TAGS, "@tags: tag1, tag2, tag3\n").is_err(),
true,
);
assert_eq2!(
parse_csv_opt_eol(TAGS, "@tags: tag1,tag2, tag3\n").is_err(),
true,
);
assert_eq2!(
parse_csv_opt_eol(TAGS, "@tags: tag1, tag2,tag3\n").is_err(),
true,
);
assert_eq2!(
parse_csv_opt_eol(TAGS, "@tags: tag1, tag2,tag3\n").is_err(),
true,
);
assert_eq2!(
parse_csv_opt_eol(TAGS, "@tags: tag1, tag2, tag3\n").unwrap(),
("", list!["tag1", "tag2", " tag3"]),
);
}
#[test]
fn test_not_quoted_with_postfix_content_1() {
let input = "@tags: \nfoo\nbar";
let (input, output) = parse_csv_opt_eol(TAGS, input).unwrap();
assert_eq2!(input, "foo\nbar");
assert_eq2!(output, list![]);
}
}