use std::borrow::Cow;
pub fn parse_message<'a>(
message: &str,
arg_provider: impl Fn(&str) -> Option<Cow<'a, str>> + 'a,
) -> String {
let mut result = String::with_capacity(message.len());
let mut chars = message.chars().peekable();
while let Some(c) = chars.next() {
match c {
'{' => {
if chars.peek() == Some(&'{') {
chars.next(); result.push('{');
continue;
}
match collect_until_close_brace(&mut chars) {
PlaceholderResult::Valid(arg_name) => {
if !arg_name.is_empty()
&& let Some(val) = arg_provider(&arg_name)
{
result.push_str(&val);
} else {
result.push('{');
result.push_str(&arg_name);
result.push('}');
}
}
PlaceholderResult::Incomplete(arg_name) => {
result.push('{');
result.push_str(&arg_name);
}
PlaceholderResult::Empty => {
result.push('{');
}
}
}
'}' => {
if chars.peek() == Some(&'}') {
chars.next(); result.push('}');
} else {
result.push(c);
}
}
c => result.push(c),
}
}
result
}
enum PlaceholderResult {
Valid(String),
Incomplete(String),
Empty,
}
fn collect_until_close_brace(
chars: &mut std::iter::Peekable<std::str::Chars>,
) -> PlaceholderResult {
let mut arg_name = String::new();
while let Some(&next_char) = chars.peek() {
match next_char {
'}' => {
chars.next();
if chars.peek() == Some(&'}') {
chars.next();
arg_name.push('}');
} else {
return PlaceholderResult::Valid(arg_name);
}
}
_ => {
arg_name.push(chars.next().unwrap());
}
}
}
if arg_name.is_empty() {
PlaceholderResult::Empty
} else {
PlaceholderResult::Incomplete(arg_name)
}
}