use crate::{Item, ParseError, Template};
impl<'s> Template<'s> {
pub(crate) fn parse_items(source: &'s str) -> Result<Vec<Item<'s>>, ParseError> {
let mut items = Vec::new();
let mut start = 0;
let mut s = source;
loop {
if let Some(index) = s.find(['\\', '{', '}']) {
if index != 0 {
let (first, last) = s.split_at(index);
items.push(Item::Text(first));
start += index;
s = last;
}
} else {
if !s.is_empty() {
items.push(Item::Text(s));
}
break Ok(items);
};
let mut chars = s.chars();
let ch = chars.next().unwrap();
match ch {
'\\' => {
match chars.next() {
Some('\\' | '{' | '}') => {
let t = s.get(1..2).unwrap();
debug_assert!(["\\", "{", "}"].contains(&t), "{}", t);
items.push(Item::Text(t));
start += 2;
s = s.get(2..).unwrap();
}
_ => {
return Err(ParseError::escape(source, start, start + 1));
}
}
}
'{' => {
let Some((key, rest)) = s[1..].split_once('}') else {
return Err(ParseError::unbalanced(source, start, start + s.len()));
};
if let Some(index) = key.find('\\') {
return Err(ParseError::key_escape(source, start, start + 1 + index));
}
let k = key.trim();
if k.is_empty() {
return Err(ParseError::key_empty(source, start, start + key.len() + 1));
}
items.push(Item::Key(k));
start += 1 + key.len() + 1;
s = rest;
}
'}' => {
return Err(ParseError::unbalanced(source, start, start));
}
_ => unreachable!(),
}
}
}
}
#[cfg(test)]
mod test_valid {
use crate::{template, Template};
#[test]
fn empty() {
let template = Template::parse("").unwrap();
assert_eq!(template, Template::default());
}
#[test]
fn no_keys() {
let template = Template::parse("hello world").unwrap();
assert_eq!(template, template!("hello world"));
}
#[test]
fn leading_key() {
let template = Template::parse("{salutation} world").unwrap();
assert_eq!(template, template!({ "salutation" }, " world"));
}
#[test]
fn trailing_key() {
let template = Template::parse("hello {name}").unwrap();
assert_eq!(template, template!("hello ", { "name" }));
}
#[test]
fn middle_key() {
let template = Template::parse("hello {name}!").unwrap();
assert_eq!(template, template!("hello ", { "name" }, "!"));
}
#[test]
fn middle_text() {
let template = Template::parse("{salutation} good {title}").unwrap();
assert_eq!(template, template!({ "salutation" }, " good ", { "title" }));
}
#[test]
fn multiline() {
let template = Template::parse(
"
And if thy native country was { ancient civilisation },
What need to slight thee? Came not {hero} thence,
Who gave to { country } her books and art of writing?
",
)
.unwrap();
assert_eq!(
template,
template!(
"\n And if thy native country was ",
{ "ancient civilisation" },
",\n What need to slight thee? Came not ",
{ "hero" },
" thence,\n Who gave to ",
{ "country" },
" her books and art of writing?\n ",
)
);
}
#[test]
fn key_no_whitespace() {
let template = Template::parse("{word}").unwrap();
assert_eq!(template, template!({ "word" }));
}
#[test]
fn key_leading_whitespace() {
let template = Template::parse("{ word}").unwrap();
assert_eq!(template, template!({ "word" }));
}
#[test]
fn key_trailing_whitespace() {
let template = Template::parse("{word\n}").unwrap();
assert_eq!(template, template!({ "word" }));
}
#[test]
fn key_both_whitespace() {
let template = Template::parse(
"{
\tword
}",
)
.unwrap();
assert_eq!(template, template!({ "word" }));
}
#[test]
fn key_inner_whitespace() {
let template = Template::parse("{ a word }").unwrap();
assert_eq!(template, template!({ "a word" }));
}
#[test]
fn escape_left() {
let template = Template::parse(r"this \{ single left brace").unwrap();
assert_eq!(template, template!("this ", "{", " single left brace"));
}
#[test]
fn escape_right() {
let template = Template::parse(r"this \} single right brace").unwrap();
assert_eq!(template, template!("this ", "}", " single right brace"));
}
#[test]
fn escape_both() {
let template = Template::parse(r"these \{ two \} braces").unwrap();
assert_eq!(template, template!("these ", "{", " two ", "}", " braces"));
}
#[test]
fn escape_doubled() {
let template = Template::parse(r"these \{\{ four \}\} braces").unwrap();
assert_eq!(
template,
template!("these ", "{", "{", " four ", "}", "}", " braces")
);
}
#[test]
fn escape_escape() {
let template = Template::parse(r"these \\ backslashes \\\\").unwrap();
assert_eq!(
template,
template!("these ", r"\", " backslashes ", r"\", r"\",)
);
}
#[test]
fn escape_before_key() {
let template = Template::parse(r"\\{ a } \{{ b } \}{ c }").unwrap();
assert_eq!(
template,
template!(r"\", { "a" }, " ", r"{", { "b" }, " ", r"}", { "c" })
);
}
#[test]
fn escape_after_key() {
let template = Template::parse(r"{ a }\\ { b }\{ { c }\}").unwrap();
assert_eq!(
template,
template!({ "a" }, r"\", " ", { "b" }, r"{", " ", { "c" }, r"}")
);
}
#[test]
fn multibyte_texts() {
let template = Template::parse("幸徳 {particle} 秋水").unwrap();
assert_eq!(template, template!("幸徳 ", { "particle" }, " 秋水"));
}
#[test]
fn multibyte_key() {
let template = Template::parse("The { 連盟 }").unwrap();
assert_eq!(template, template!("The ", { "連盟" }));
}
#[test]
fn multibyte_both() {
let template = Template::parse("大杉 {栄}").unwrap();
assert_eq!(template, template!("大杉 ", { "栄" }));
}
#[test]
fn multibyte_whitespace() {
let template = Template::parse("岩佐 作{ 太 }郎").unwrap();
assert_eq!(template, template!("岩佐 作", { "太" }, "郎"));
}
#[test]
fn multibyte_with_escapes() {
let template = Template::parse(r"日本\{アナキスト\}連盟").unwrap();
assert_eq!(
template,
template!("日本", r"{", "アナキスト", r"}", "連盟")
);
}
#[test]
fn multibyte_rtl_text() {
let template = Template::parse("محمد صايل").unwrap();
assert_eq!(template, template!("محمد صايل"));
}
#[test]
fn multibyte_rtl_key() {
let template = Template::parse("محمد {ريشة}").unwrap();
assert_eq!(template, template!("محمد ", { "ريشة" }));
}
}
#[cfg(test)]
mod test_error {
use crate::{ParseError, Template};
#[test]
fn key_left_half() {
let template = Template::parse("{ open").unwrap_err();
assert_eq!(template, ParseError::unbalanced("{ open", 0, 6));
}
#[test]
fn key_right_half() {
let template = Template::parse("open }").unwrap_err();
assert_eq!(template, ParseError::unbalanced("open }", 5, 5));
}
#[test]
fn key_with_half_escape() {
let template = Template::parse(r"this is { not \ allowed }").unwrap_err();
assert_eq!(
template,
ParseError::key_escape(r"this is { not \ allowed }", 8, 14)
);
}
#[test]
fn key_with_full_escape() {
let template = Template::parse(r"{ not \} allowed }").unwrap_err();
assert_eq!(
template,
ParseError::key_escape(r"{ not \} allowed }", 0, 6)
);
}
#[test]
fn key_empty() {
let template = Template::parse(r"void: {}").unwrap_err();
assert_eq!(template, ParseError::key_empty(r"void: {}", 6, 7));
}
#[test]
fn key_only_whitespace() {
let template = Template::parse(r"nothing: { }").unwrap_err();
assert_eq!(template, ParseError::key_empty(r"nothing: { }", 9, 11));
}
#[test]
fn bad_escape() {
let template = Template::parse(r"not \a thing").unwrap_err();
assert_eq!(template, ParseError::escape(r"not \a thing", 4, 5));
}
#[test]
fn end_escape() {
let template = Template::parse(r"forget me not \").unwrap_err();
assert_eq!(template, ParseError::escape(r"forget me not \", 14, 15));
}
}