pub(crate) fn substitute(template: &str, replacements: &[(&str, &str)]) -> String {
let cap = template
.len()
.saturating_add(replacements.iter().map(|(_, v)| v.len()).sum());
let mut out = String::with_capacity(cap);
let mut pos = 0;
while pos < template.len() {
match template[pos..].find("{{") {
None => {
out.push_str(&template[pos..]);
break;
}
Some(rel_start) => {
let abs_start = pos + rel_start;
out.push_str(&template[pos..abs_start]);
let mut matched_len = 0;
for (key, val) in replacements {
if template[abs_start..].starts_with(*key) {
out.push_str(val);
matched_len = key.len();
break;
}
}
if matched_len == 0 {
out.push_str("{{");
pos = abs_start + 2;
} else {
pos = abs_start + matched_len;
}
}
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn substitute_replaces_simple_keys() {
let html = substitute(
"<title>{{TITLE}}</title>",
&[("{{TITLE}}", "CodeLore Dashboard")],
);
assert_eq!(html, "<title>CodeLore Dashboard</title>");
}
#[test]
fn substitute_replaces_multiple_disjoint_keys() {
let html = substitute(
"{{A}}-{{B}}-{{A}}",
&[("{{A}}", "alpha"), ("{{B}}", "beta")],
);
assert_eq!(html, "alpha-beta-alpha");
}
#[test]
fn substitute_leaves_unknown_placeholders_verbatim() {
let html = substitute("{{KNOWN}}-{{UNKNOWN}}", &[("{{KNOWN}}", "x")]);
assert_eq!(html, "x-{{UNKNOWN}}");
}
#[test]
fn substitute_passes_through_text_with_no_placeholders() {
let html = substitute("no placeholders here", &[("{{X}}", "y")]);
assert_eq!(html, "no placeholders here");
}
#[test]
fn substitute_handles_value_that_contains_double_braces() {
let html = substitute("{{A}}-end", &[("{{A}}", "<{{NESTED}}>")]);
assert_eq!(html, "<{{NESTED}}>-end");
}
#[test]
fn substitute_first_match_wins_on_overlap() {
let html = substitute("{{X}}", &[("{{X}}", "first"), ("{{X}}", "second")]);
assert_eq!(html, "first");
}
}