From e6ced548422af625778c31e9e5a5716a70b84aed Mon Sep 17 00:00:00 2001 From: Oliver Hamlet Date: Fri, 25 Jul 2025 22:45:30 +0100 Subject: [PATCH] Improve handling of invalid placeholders during message substitution --- src/metadata/message.rs | 84 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 80 insertions(+), 4 deletions(-) diff --git a/src/metadata/message.rs b/src/metadata/message.rs index c89badcd..8fb5db89 100644 --- a/src/metadata/message.rs +++ b/src/metadata/message.rs @@ -327,8 +327,17 @@ fn format(text: &str, subs: &[&str]) -> Result, MetadataParsingErrorRea for slice in text.split_inclusive(['{', '}']) { if let Some(prefix) = slice.strip_suffix('{') { - new_text.push_str(prefix); - maybe_in_placeholder = true; + if maybe_in_placeholder { + if prefix.is_empty() { + new_text.push_str(slice); + } else { + new_text.push('{'); + new_text.push_str(prefix); + } + } else { + new_text.push_str(prefix); + maybe_in_placeholder = true; + } } else if let Some(prefix) = slice.strip_suffix('}') { if maybe_in_placeholder { if let Ok(sub_index) = prefix.parse::() { @@ -343,18 +352,27 @@ fn format(text: &str, subs: &[&str]) -> Result, MetadataParsingErrorRea unused_sub_indexes.remove(&sub_index); } else { // Not a valid placeholder, treat it as normal text. - new_text.push_str(prefix); + new_text.push('{'); + new_text.push_str(slice); } maybe_in_placeholder = false; } else { - new_text.push_str(prefix); + new_text.push_str(slice); } } else { + if maybe_in_placeholder { + new_text.push('{'); + maybe_in_placeholder = false; + } new_text.push_str(slice); } } + if maybe_in_placeholder { + new_text.push('{'); + } + if let Some(sub_index) = unused_sub_indexes.first() { if let Some(sub) = subs.get(*sub_index) { return Err(MetadataParsingErrorReason::MissingPlaceholder( @@ -742,6 +760,64 @@ mod tests { } } + mod format { + use super::*; + + #[test] + fn should_treat_invalid_placeholders_as_literals() { + let format_no_subs = |i| format(i, &[]).unwrap(); + + let input = "a{"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a{a"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a{}"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a{a}"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a}"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a{ }"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a{0a}"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a{{"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a{b{"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a{b{c"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a}}b"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a}b}"; + assert_eq!(input, format_no_subs(input).as_ref()); + + let input = "a}b}c"; + assert_eq!(input, format_no_subs(input).as_ref()); + + assert_eq!("a{b", format("a{{0}", &["b"]).unwrap().as_ref()); + + assert_eq!("ab}", format("a{0}}", &["b"]).unwrap().as_ref()); + } + + #[test] + fn allows_leading_zeroes_in_placeholders() { + let input = "a{0}-{01}"; + assert_eq!("ab-c", format(input, &["b", "c"]).unwrap().as_ref()); + } + } + mod emit_yaml { use super::*;