1pub fn fill(template: &str, values: &[(&str, &str)]) -> String {
14 debug_assert!(
15 values
16 .iter()
17 .all(|(name, _)| name.len() > 2 && name.starts_with('{') && name.ends_with('}')),
18 "a placeholder name is written with its braces: {values:?}"
19 );
20 let mut out = String::with_capacity(template.len());
21 let mut rest = template;
22 while let Some(at) = rest.find('{') {
23 out.push_str(&rest[..at]);
24 rest = &rest[at..];
25 match values.iter().find(|(name, _)| rest.starts_with(name)) {
26 Some((name, value)) => {
27 out.push_str(value);
28 rest = &rest[name.len()..];
29 }
30 None => {
31 out.push('{');
32 rest = &rest[1..];
33 }
34 }
35 }
36 out.push_str(rest);
37 out
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn a_value_naming_a_placeholder_is_not_filled_again() {
46 let out = fill("{a} and {b}", &[("{a}", "says {b}"), ("{b}", "says {a}")]);
47 assert_eq!("says {b} and says {a}", out);
48 }
49
50 #[test]
51 fn every_occurrence_in_the_template_is_filled() {
52 assert_eq!("1, 1", fill("{n}, {n}", &[("{n}", "1")]));
53 }
54
55 #[test]
56 fn braces_that_name_nothing_are_kept() {
57 assert_eq!(
58 "{ x } {other} {n",
59 fill("{ x } {other} {n", &[("{n}", "1")])
60 );
61 assert_eq!("{1}", fill("{{n}}", &[("{n}", "1")]));
62 }
63
64 #[test]
65 fn text_around_a_placeholder_survives_multibyte_characters() {
66 assert_eq!("é: ü.", fill("é: {v}.", &[("{v}", "ü")]));
67 }
68}