pub fn interpolate(template: &str, params: &[(&str, &str)]) -> String {
if params.is_empty() {
return template.to_string();
}
let mut sorted: Vec<(&str, &str)> = params.to_vec();
sorted.sort_by(|a, b| b.0.len().cmp(&a.0.len()));
let mut result = template.to_string();
for (key, value) in &sorted {
let upper_placeholder = format!(":{}", key.to_uppercase());
if result.contains(&upper_placeholder) {
result = result.replace(&upper_placeholder, &value.to_uppercase());
}
let ucfirst_placeholder = format!(":{}", ucfirst(key));
if result.contains(&ucfirst_placeholder) {
result = result.replace(&ucfirst_placeholder, &ucfirst(value));
}
let placeholder = format!(":{key}");
if result.contains(&placeholder) {
result = result.replace(&placeholder, value);
}
}
result
}
fn ucfirst(s: &str) -> String {
let mut chars = s.chars();
match chars.next() {
None => String::new(),
Some(c) => {
let upper: String = c.to_uppercase().collect();
format!("{}{}", upper, chars.as_str())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_replacement() {
let result = interpolate("Hello :name!", &[("name", "world")]);
assert_eq!(result, "Hello world!");
}
#[test]
fn multiple_params() {
let result = interpolate(
"The :attribute must be at least :min characters.",
&[("attribute", "password"), ("min", "8")],
);
assert_eq!(result, "The password must be at least 8 characters.");
}
#[test]
fn ucfirst_variant() {
let result = interpolate("Hello :Name!", &[("name", "john")]);
assert_eq!(result, "Hello John!");
}
#[test]
fn uppercase_variant() {
let result = interpolate("Hello :NAME!", &[("name", "john")]);
assert_eq!(result, "Hello JOHN!");
}
#[test]
fn all_three_variants() {
let result = interpolate(":name :Name :NAME", &[("name", "alice")]);
assert_eq!(result, "alice Alice ALICE");
}
#[test]
fn missing_param_leaves_placeholder() {
let result = interpolate("Hello :name!", &[]);
assert_eq!(result, "Hello :name!");
}
#[test]
fn empty_params_returns_template() {
let result = interpolate("No placeholders here", &[]);
assert_eq!(result, "No placeholders here");
}
#[test]
fn longer_key_first() {
let result = interpolate(":user and :username", &[("user", "A"), ("username", "B")]);
assert_eq!(result, "A and B");
}
}