diff --git a/crates/snapbox/src/substitutions.rs b/crates/snapbox/src/substitutions.rs index 6798906c..1c01b50b 100644 --- a/crates/snapbox/src/substitutions.rs +++ b/crates/snapbox/src/substitutions.rs @@ -84,23 +84,21 @@ impl Substitutions { normalize(input, pattern, self) } - fn substitute<'v>(&self, value: &'v str) -> Cow<'v, str> { - let mut value = Cow::Borrowed(value); - for (var, replace) in self.vars.iter() { - for replace in replace { - debug_assert!(!replace.is_empty()); - value = Cow::Owned(value.replace(replace.as_ref(), var)); - } - } - value + fn substitute<'v>(&self, input: &'v str) -> String { + let mut input = input.to_owned(); + replace_many( + &mut input, + self.vars.iter().flat_map(|(var, replaces)| { + replaces.iter().map(|replace| (*var, replace.as_ref())) + }), + ); + input } fn clear<'v>(&self, pattern: &'v str) -> Cow<'v, str> { if !self.unused.is_empty() && pattern.contains('[') { let mut pattern = pattern.to_owned(); - for var in self.unused.iter() { - pattern = pattern.replace(var, ""); - } + replace_many(&mut pattern, self.unused.iter().map(|var| (*var, ""))); Cow::Owned(pattern) } else { Cow::Borrowed(pattern) @@ -108,6 +106,20 @@ impl Substitutions { } } +fn replace_many<'a>( + buffer: &mut String, + replacements: impl IntoIterator, +) { + for (var, replace) in replacements { + let mut index = 0; + while let Some(offset) = buffer[index..].find(var) { + let old_range = (index + offset)..(index + offset + var.len()); + buffer.replace_range(old_range, replace); + index += offset + replace.len(); + } + } +} + fn validate_key(key: &'static str) -> Result<&'static str, crate::Error> { if !key.starts_with('[') || !key.ends_with(']') { return Err(format!("Key `{}` is not enclosed in []", key).into());