Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

functions: add Replace, and RegexpReplace from Terraform #45

Merged
merged 6 commits into from
Mar 9, 2020
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions cty/function/stdlib/string_replace.go
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()

azr marked this conversation as resolved.
Show resolved Hide resolved
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})
}
101 changes: 101 additions & 0 deletions cty/function/stdlib/string_replace_test.go
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/"),
azr marked this conversation as resolved.
Show resolved Hide resolved
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")
}
}