feat(ansible): add regex_replace filter support

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-04-02 01:51:39 +00:00
parent 6c1c7d9bd4
commit b3f2cc3fc6
3 changed files with 86 additions and 0 deletions

View file

@ -2654,6 +2654,20 @@ func (e *Executor) applyFilter(value, filter string) string {
return corexTrimSpace(value)
}
// Handle regex_replace
if corexHasPrefix(filter, "regex_replace(") {
pattern, replacement, ok := parseRegexReplaceFilter(filter)
if !ok {
return value
}
compiled, err := regexp.Compile(pattern)
if err != nil {
return value
}
return compiled.ReplaceAllString(value, replacement)
}
// Handle b64decode
if filter == "b64decode" {
decoded, err := base64.StdEncoding.DecodeString(value)
@ -2669,6 +2683,65 @@ func (e *Executor) applyFilter(value, filter string) string {
return value
}
func parseRegexReplaceFilter(filter string) (string, string, bool) {
if !corexHasPrefix(filter, "regex_replace(") || !corexHasSuffix(filter, ")") {
return "", "", false
}
args := strings.TrimSpace(filter[len("regex_replace(") : len(filter)-1])
parts := splitFilterArgs(args)
if len(parts) < 2 {
return "", "", false
}
return trimCutset(parts[0], "'\""), trimCutset(parts[1], "'\""), true
}
func splitFilterArgs(args string) []string {
if args == "" {
return nil
}
var (
parts []string
current strings.Builder
inSingle bool
inDouble bool
escaped bool
)
for _, r := range args {
switch {
case escaped:
current.WriteRune(r)
escaped = false
case r == '\\' && (inSingle || inDouble):
current.WriteRune(r)
escaped = true
case r == '\'' && !inDouble:
current.WriteRune(r)
inSingle = !inSingle
case r == '"' && !inSingle:
current.WriteRune(r)
inDouble = !inDouble
case r == ',' && !inSingle && !inDouble:
part := strings.TrimSpace(current.String())
if part != "" {
parts = append(parts, part)
}
current.Reset()
default:
current.WriteRune(r)
}
}
if tail := strings.TrimSpace(current.String()); tail != "" {
parts = append(parts, tail)
}
return parts
}
func isUnresolvedTemplateValue(value string) bool {
return corexHasPrefix(value, "{{ ") && corexHasSuffix(value, " }}")
}

View file

@ -1707,6 +1707,13 @@ func TestExecutor_ApplyFilter_Good_Trim(t *testing.T) {
assert.Equal(t, "hello", e.applyFilter(" hello ", "trim"))
}
func TestExecutor_ApplyFilter_Good_RegexReplace(t *testing.T) {
e := NewExecutor("/tmp")
assert.Equal(t, "web-01", e.applyFilter("web_01", "regex_replace('_', '-')"))
assert.Equal(t, "123", e.applyFilter("abc123", `regex_replace("\D+", "")`))
}
// --- resolveLoop ---
func TestExecutor_ResolveLoop_Good_SliceAny(t *testing.T) {

View file

@ -499,6 +499,12 @@ func TestModulesInfra_ApplyFilter_Good_TrimFilter(t *testing.T) {
assert.Equal(t, "", e.applyFilter(" ", "trim"))
}
func TestModulesInfra_ApplyFilter_Good_RegexReplaceFilter(t *testing.T) {
e := NewExecutor("/tmp")
assert.Equal(t, "app-01", e.applyFilter("app_01", "regex_replace('_', '-')"))
assert.Equal(t, "42", e.applyFilter("v42", `regex_replace("^v", "")`))
}
func TestModulesInfra_ApplyFilter_Good_B64Decode(t *testing.T) {
e := NewExecutor("/tmp")
assert.Equal(t, "test", e.applyFilter("dGVzdA==", "b64decode"))