-
-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
function/stdlib: New "Replace" and "RegexpReplace" functions
These are inspired by (but not fully compatible with) the single "replace" function in HashiCorp Terraform. Here we prefer to have two separate functions for selecting regular expression or plain string matching, where as the Terraform function uses special syntax in the matching string to activate regex mode.
- Loading branch information
Showing
2 changed files
with
181 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package stdlib | ||
|
||
import ( | ||
"regexp" | ||
"strings" | ||
|
||
"github.com/zclconf/go-cty/cty" | ||
"github.com/zclconf/go-cty/cty/function" | ||
) | ||
|
||
// ReplaceFunc is a function that searches a given string for another given | ||
// substring, and replaces each occurence with a given replacement string. | ||
// The substr argument is a simple string. | ||
var ReplaceFunc = function.New(&function.Spec{ | ||
Params: []function.Parameter{ | ||
{ | ||
Name: "str", | ||
Type: cty.String, | ||
}, | ||
{ | ||
Name: "substr", | ||
Type: cty.String, | ||
}, | ||
{ | ||
Name: "replace", | ||
Type: cty.String, | ||
}, | ||
}, | ||
Type: function.StaticReturnType(cty.String), | ||
Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { | ||
str := args[0].AsString() | ||
substr := args[1].AsString() | ||
replace := args[2].AsString() | ||
|
||
return cty.StringVal(strings.Replace(str, substr, replace, -1)), nil | ||
}, | ||
}) | ||
|
||
// RegexpReplaceFunc is a function that searches a given string for another | ||
// given substring, and replaces each occurence with a given replacement | ||
// string. The substr argument must be a valid regular expression. | ||
var RegexpReplaceFunc = function.New(&function.Spec{ | ||
Params: []function.Parameter{ | ||
{ | ||
Name: "str", | ||
Type: cty.String, | ||
}, | ||
{ | ||
Name: "substr", | ||
Type: cty.String, | ||
}, | ||
{ | ||
Name: "replace", | ||
Type: cty.String, | ||
}, | ||
}, | ||
Type: function.StaticReturnType(cty.String), | ||
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { | ||
str := args[0].AsString() | ||
substr := args[1].AsString() | ||
replace := args[2].AsString() | ||
|
||
re, err := regexp.Compile(substr) | ||
if err != nil { | ||
return cty.UnknownVal(cty.String), err | ||
} | ||
|
||
return cty.StringVal(re.ReplaceAllString(str, replace)), nil | ||
}, | ||
}) | ||
|
||
// Replace searches a given string for another given substring, | ||
// and replaces all occurrences with a given replacement string. | ||
func Replace(str, substr, replace cty.Value) (cty.Value, error) { | ||
return ReplaceFunc.Call([]cty.Value{str, substr, replace}) | ||
} | ||
|
||
func RegexpReplace(str, substr, replace cty.Value) (cty.Value, error) { | ||
return RegexpReplaceFunc.Call([]cty.Value{str, substr, replace}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package stdlib | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/zclconf/go-cty/cty" | ||
) | ||
|
||
func TestReplace(t *testing.T) { | ||
tests := []struct { | ||
Input cty.Value | ||
Substr, Replace cty.Value | ||
Want cty.Value | ||
}{ | ||
{ | ||
cty.StringVal("hello"), | ||
cty.StringVal("l"), | ||
cty.StringVal(""), | ||
cty.StringVal("heo"), | ||
}, | ||
{ | ||
cty.StringVal("😸😸😸😾😾😾"), | ||
cty.StringVal("😾"), | ||
cty.StringVal("😸"), | ||
cty.StringVal("😸😸😸😸😸😸"), | ||
}, | ||
{ | ||
cty.StringVal("😸😸😸😸😸😾"), | ||
cty.StringVal("😾"), | ||
cty.StringVal("😸"), | ||
cty.StringVal("😸😸😸😸😸😸"), | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.Input.GoString()+"_replace", func(t *testing.T) { | ||
got, err := Replace(test.Input, test.Substr, test.Replace) | ||
|
||
if err != nil { | ||
t.Fatalf("unexpected error: %s", err) | ||
} | ||
|
||
if !got.RawEquals(test.Want) { | ||
t.Fatalf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) | ||
} | ||
}) | ||
t.Run(test.Input.GoString()+"_regex_replace", func(t *testing.T) { | ||
got, err := Replace(test.Input, test.Substr, test.Replace) | ||
|
||
if err != nil { | ||
t.Fatalf("unexpected error: %s", err) | ||
} | ||
|
||
if !got.RawEquals(test.Want) { | ||
t.Fatalf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestRegexReplace(t *testing.T) { | ||
tests := []struct { | ||
Input cty.Value | ||
Substr, Replace cty.Value | ||
Want cty.Value | ||
}{ | ||
{ | ||
cty.StringVal("-ab-axxb-"), | ||
cty.StringVal("a(x*)b"), | ||
cty.StringVal("T"), | ||
cty.StringVal("-T-T-"), | ||
}, | ||
{ | ||
cty.StringVal("-ab-axxb-"), | ||
cty.StringVal("a(x*)b"), | ||
cty.StringVal("${1}W"), | ||
cty.StringVal("-W-xxW-"), | ||
}, | ||
} | ||
|
||
for _, test := range tests { | ||
t.Run(test.Input.GoString(), func(t *testing.T) { | ||
got, err := RegexpReplace(test.Input, test.Substr, test.Replace) | ||
|
||
if err != nil { | ||
t.Fatalf("unexpected error: %s", err) | ||
} | ||
|
||
if !got.RawEquals(test.Want) { | ||
t.Fatalf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestRegexReplace_invalid_regex(t *testing.T) { | ||
_, err := RegexpReplace(cty.StringVal(""), cty.StringVal("("), cty.StringVal("")) | ||
if err == nil { | ||
t.Fatal("expected an error") | ||
} | ||
} |