mirror of
https://github.com/mikefarah/yq.git
synced 2026-07-02 02:11:39 +00:00
Fixing escape charaters again 😢 #2517
This commit is contained in:
parent
5d0481c0d2
commit
9e0c5fd3c9
@ -187,15 +187,69 @@ func parseInt(numberString string) (int, error) {
|
||||
}
|
||||
|
||||
func processEscapeCharacters(original string) string {
|
||||
value := original
|
||||
value = strings.ReplaceAll(value, "\\\"", "\"")
|
||||
value = strings.ReplaceAll(value, "\\n", "\n")
|
||||
value = strings.ReplaceAll(value, "\\t", "\t")
|
||||
value = strings.ReplaceAll(value, "\\r", "\r")
|
||||
value = strings.ReplaceAll(value, "\\f", "\f")
|
||||
value = strings.ReplaceAll(value, "\\v", "\v")
|
||||
value = strings.ReplaceAll(value, "\\b", "\b")
|
||||
value = strings.ReplaceAll(value, "\\a", "\a")
|
||||
if original == "" {
|
||||
return original
|
||||
}
|
||||
|
||||
var result strings.Builder
|
||||
runes := []rune(original)
|
||||
|
||||
for i := 0; i < len(runes); i++ {
|
||||
if runes[i] == '\\' && i < len(runes)-1 {
|
||||
next := runes[i+1]
|
||||
switch next {
|
||||
case '\\':
|
||||
// Check if followed by opening bracket - if so, preserve both backslashes
|
||||
// this is required for string interpolation to work correctly.
|
||||
if i+2 < len(runes) && runes[i+2] == '(' {
|
||||
// Preserve \\ when followed by (
|
||||
result.WriteRune('\\')
|
||||
result.WriteRune('\\')
|
||||
i++ // Skip the next backslash (we'll process the ( normally on next iteration)
|
||||
continue
|
||||
}
|
||||
// Escaped backslash: \\ -> \
|
||||
result.WriteRune('\\')
|
||||
i++ // Skip the next backslash
|
||||
continue
|
||||
case '"':
|
||||
result.WriteRune('"')
|
||||
i++ // Skip the quote
|
||||
continue
|
||||
case 'n':
|
||||
result.WriteRune('\n')
|
||||
i++ // Skip the 'n'
|
||||
continue
|
||||
case 't':
|
||||
result.WriteRune('\t')
|
||||
i++ // Skip the 't'
|
||||
continue
|
||||
case 'r':
|
||||
result.WriteRune('\r')
|
||||
i++ // Skip the 'r'
|
||||
continue
|
||||
case 'f':
|
||||
result.WriteRune('\f')
|
||||
i++ // Skip the 'f'
|
||||
continue
|
||||
case 'v':
|
||||
result.WriteRune('\v')
|
||||
i++ // Skip the 'v'
|
||||
continue
|
||||
case 'b':
|
||||
result.WriteRune('\b')
|
||||
i++ // Skip the 'b'
|
||||
continue
|
||||
case 'a':
|
||||
result.WriteRune('\a')
|
||||
i++ // Skip the 'a'
|
||||
continue
|
||||
}
|
||||
}
|
||||
result.WriteRune(runes[i])
|
||||
}
|
||||
|
||||
value := result.String()
|
||||
if value != original {
|
||||
log.Debug("processEscapeCharacters from [%v] to [%v]", original, value)
|
||||
}
|
||||
|
||||
@ -408,3 +408,122 @@ func TestKindString(t *testing.T) {
|
||||
test.AssertResult(t, "AliasNode", KindString(AliasNode))
|
||||
test.AssertResult(t, "unknown!", KindString(Kind(999))) // Invalid kind
|
||||
}
|
||||
|
||||
type processEscapeCharactersScenario struct {
|
||||
input string
|
||||
expected string
|
||||
}
|
||||
|
||||
var processEscapeCharactersScenarios = []processEscapeCharactersScenario{
|
||||
{
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
input: "hello",
|
||||
expected: "hello",
|
||||
},
|
||||
{
|
||||
input: "\\\"",
|
||||
expected: "\"",
|
||||
},
|
||||
{
|
||||
input: "hello\\\"world",
|
||||
expected: "hello\"world",
|
||||
},
|
||||
{
|
||||
input: "\\n",
|
||||
expected: "\n",
|
||||
},
|
||||
{
|
||||
input: "line1\\nline2",
|
||||
expected: "line1\nline2",
|
||||
},
|
||||
{
|
||||
input: "\\t",
|
||||
expected: "\t",
|
||||
},
|
||||
{
|
||||
input: "hello\\tworld",
|
||||
expected: "hello\tworld",
|
||||
},
|
||||
{
|
||||
input: "\\r",
|
||||
expected: "\r",
|
||||
},
|
||||
{
|
||||
input: "hello\\rworld",
|
||||
expected: "hello\rworld",
|
||||
},
|
||||
{
|
||||
input: "\\f",
|
||||
expected: "\f",
|
||||
},
|
||||
{
|
||||
input: "hello\\fworld",
|
||||
expected: "hello\fworld",
|
||||
},
|
||||
{
|
||||
input: "\\v",
|
||||
expected: "\v",
|
||||
},
|
||||
{
|
||||
input: "hello\\vworld",
|
||||
expected: "hello\vworld",
|
||||
},
|
||||
{
|
||||
input: "\\b",
|
||||
expected: "\b",
|
||||
},
|
||||
{
|
||||
input: "hello\\bworld",
|
||||
expected: "hello\bworld",
|
||||
},
|
||||
{
|
||||
input: "\\a",
|
||||
expected: "\a",
|
||||
},
|
||||
{
|
||||
input: "hello\\aworld",
|
||||
expected: "hello\aworld",
|
||||
},
|
||||
{
|
||||
input: "\\\"\\n\\t\\r\\f\\v\\b\\a",
|
||||
expected: "\"\n\t\r\f\v\b\a",
|
||||
},
|
||||
{
|
||||
input: "multiple\\nlines\\twith\\ttabs",
|
||||
expected: "multiple\nlines\twith\ttabs",
|
||||
},
|
||||
{
|
||||
input: "quote\\\"here",
|
||||
expected: "quote\"here",
|
||||
},
|
||||
{
|
||||
input: "\\\\",
|
||||
expected: "\\", // Backslash is processed: "\\\\" becomes "\\"
|
||||
},
|
||||
{
|
||||
input: "\\\"test\\\"",
|
||||
expected: "\"test\"",
|
||||
},
|
||||
{
|
||||
input: "a\\\\b",
|
||||
expected: "a\\b", // Tests roundtrip: "a\\\\b" should become "a\\b"
|
||||
},
|
||||
{
|
||||
input: "Hi \\\\(.value)",
|
||||
expected: "Hi \\\\(.value)",
|
||||
},
|
||||
{
|
||||
input: `a\\b`,
|
||||
expected: "a\\b",
|
||||
},
|
||||
}
|
||||
|
||||
func TestProcessEscapeCharacters(t *testing.T) {
|
||||
for _, tt := range processEscapeCharactersScenarios {
|
||||
actual := processEscapeCharacters(tt.input)
|
||||
test.AssertResultComplexWithContext(t, tt.expected, actual, fmt.Sprintf("Input: %q", tt.input))
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user