porting many functions
This commit is contained in:
parent
083ea05295
commit
10ef61c71c
|
@ -8,6 +8,7 @@ import (
|
||||||
|
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
"github.com/zclconf/go-cty/cty/function"
|
"github.com/zclconf/go-cty/cty/function"
|
||||||
|
"github.com/zclconf/go-cty/cty/gocty"
|
||||||
)
|
)
|
||||||
|
|
||||||
var JoinFunc = function.New(&function.Spec{
|
var JoinFunc = function.New(&function.Spec{
|
||||||
|
@ -126,6 +127,99 @@ var ChompFunc = function.New(&function.Spec{
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// IndentFunc constructions a function that adds a given number of spaces to the
|
||||||
|
// beginnings of all but the first line in a given multi-line string.
|
||||||
|
var IndentFunc = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{
|
||||||
|
{
|
||||||
|
Name: "spaces",
|
||||||
|
Type: cty.Number,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "str",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.String),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
|
var spaces int
|
||||||
|
if err := gocty.FromCtyValue(args[0], &spaces); err != nil {
|
||||||
|
return cty.UnknownVal(cty.String), err
|
||||||
|
}
|
||||||
|
data := args[1].AsString()
|
||||||
|
pad := strings.Repeat(" ", spaces)
|
||||||
|
return cty.StringVal(strings.Replace(data, "\n", "\n"+pad, -1)), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// ReplaceFunc constructions a function that searches a given string for another
|
||||||
|
// given substring, and replaces each occurence with a given replacement 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) (ret cty.Value, err error) {
|
||||||
|
str := args[0].AsString()
|
||||||
|
substr := args[1].AsString()
|
||||||
|
replace := args[2].AsString()
|
||||||
|
|
||||||
|
// We search/replace using a regexp if the string is surrounded
|
||||||
|
// in forward slashes.
|
||||||
|
if len(substr) > 1 && substr[0] == '/' && substr[len(substr)-1] == '/' {
|
||||||
|
re, err := regexp.Compile(substr[1 : len(substr)-1])
|
||||||
|
if err != nil {
|
||||||
|
return cty.UnknownVal(cty.String), err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.StringVal(re.ReplaceAllString(str, replace)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return cty.StringVal(strings.Replace(str, substr, replace, -1)), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// TitleFunc constructions a function that converts the first letter of each word
|
||||||
|
// in the given string to uppercase.
|
||||||
|
var TitleFunc = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{
|
||||||
|
{
|
||||||
|
Name: "str",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.String),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
|
return cty.StringVal(strings.Title(args[0].AsString())), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// TrimSpaceFunc constructions a function that removes any space characters from
|
||||||
|
// the start and end of the given string.
|
||||||
|
var TrimSpaceFunc = function.New(&function.Spec{
|
||||||
|
Params: []function.Parameter{
|
||||||
|
{
|
||||||
|
Name: "str",
|
||||||
|
Type: cty.String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Type: function.StaticReturnType(cty.String),
|
||||||
|
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
|
||||||
|
return cty.StringVal(strings.TrimSpace(args[0].AsString())), nil
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
// Join concatenates together the string elements of one or more lists with a
|
// Join concatenates together the string elements of one or more lists with a
|
||||||
// given separator.
|
// given separator.
|
||||||
func Join(sep cty.Value, lists ...cty.Value) (cty.Value, error) {
|
func Join(sep cty.Value, lists ...cty.Value) (cty.Value, error) {
|
||||||
|
@ -151,3 +245,25 @@ func Split(sep, str cty.Value) (cty.Value, error) {
|
||||||
func Chomp(str cty.Value) (cty.Value, error) {
|
func Chomp(str cty.Value) (cty.Value, error) {
|
||||||
return ChompFunc.Call([]cty.Value{str})
|
return ChompFunc.Call([]cty.Value{str})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Indent adds a given number of spaces to the beginnings of all but the first
|
||||||
|
// line in a given multi-line string.
|
||||||
|
func Indent(spaces, str cty.Value) (cty.Value, error) {
|
||||||
|
return IndentFunc.Call([]cty.Value{spaces, str})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replace searches a given string for another given substring,
|
||||||
|
// and replaces all occurences with a given replacement string.
|
||||||
|
func Replace(str, substr, replace cty.Value) (cty.Value, error) {
|
||||||
|
return ReplaceFunc.Call([]cty.Value{str, substr, replace})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Title converts the first letter of each word in the given string to uppercase.
|
||||||
|
func Title(str cty.Value) (cty.Value, error) {
|
||||||
|
return TitleFunc.Call([]cty.Value{str})
|
||||||
|
}
|
||||||
|
|
||||||
|
// TrimSpace removes any space characters from the start and end of the given string.
|
||||||
|
func TrimSpace(str cty.Value) (cty.Value, error) {
|
||||||
|
return TrimSpaceFunc.Call([]cty.Value{str})
|
||||||
|
}
|
||||||
|
|
|
@ -307,3 +307,202 @@ func TestChomp(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIndent(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
String cty.Value
|
||||||
|
Spaces cty.Value
|
||||||
|
Want cty.Value
|
||||||
|
Err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.StringVal(`Fleas:
|
||||||
|
Adam
|
||||||
|
Had'em
|
||||||
|
|
||||||
|
E.E. Cummings`),
|
||||||
|
cty.NumberIntVal(4),
|
||||||
|
cty.StringVal("Fleas:\n Adam\n Had'em\n \n E.E. Cummings"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("oneliner"),
|
||||||
|
cty.NumberIntVal(4),
|
||||||
|
cty.StringVal("oneliner"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal(`#!/usr/bin/env bash
|
||||||
|
date
|
||||||
|
pwd`),
|
||||||
|
cty.NumberIntVal(4),
|
||||||
|
cty.StringVal("#!/usr/bin/env bash\n date\n pwd"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("indent(%#v, %#v)", test.Spaces, test.String), func(t *testing.T) {
|
||||||
|
got, err := Indent(test.Spaces, test.String)
|
||||||
|
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(test.Want) {
|
||||||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReplace(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
String cty.Value
|
||||||
|
Substr cty.Value
|
||||||
|
Replace cty.Value
|
||||||
|
Want cty.Value
|
||||||
|
Err bool
|
||||||
|
}{
|
||||||
|
{ // Regular search and replace
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
cty.StringVal("hel"),
|
||||||
|
cty.StringVal("bel"),
|
||||||
|
cty.StringVal("bello"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // Search string doesn't match
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
cty.StringVal("nope"),
|
||||||
|
cty.StringVal("bel"),
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // Regular expression
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
cty.StringVal("/l/"),
|
||||||
|
cty.StringVal("L"),
|
||||||
|
cty.StringVal("heLLo"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("helo"),
|
||||||
|
cty.StringVal("/(l)/"),
|
||||||
|
cty.StringVal("$1$1"),
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{ // Bad regexp
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
cty.StringVal("/(l/"),
|
||||||
|
cty.StringVal("$1$1"),
|
||||||
|
cty.UnknownVal(cty.String),
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("replace(%#v, %#v, %#v)", test.String, test.Substr, test.Replace), func(t *testing.T) {
|
||||||
|
got, err := Replace(test.String, test.Substr, test.Replace)
|
||||||
|
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(test.Want) {
|
||||||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTitle(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
String cty.Value
|
||||||
|
Want cty.Value
|
||||||
|
Err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
cty.StringVal("Hello"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal("hello world"),
|
||||||
|
cty.StringVal("Hello World"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal(""),
|
||||||
|
cty.StringVal(""),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("title(%#v)", test.String), func(t *testing.T) {
|
||||||
|
got, err := Title(test.String)
|
||||||
|
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(test.Want) {
|
||||||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTrimSpace(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
String cty.Value
|
||||||
|
Want cty.Value
|
||||||
|
Err bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
cty.StringVal(" hello "),
|
||||||
|
cty.StringVal("hello"),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
cty.StringVal(""),
|
||||||
|
cty.StringVal(""),
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("trimspace(%#v)", test.String), func(t *testing.T) {
|
||||||
|
got, err := TrimSpace(test.String)
|
||||||
|
|
||||||
|
if test.Err {
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("succeeded; want error")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if err != nil {
|
||||||
|
t.Fatalf("unexpected error: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !got.RawEquals(test.Want) {
|
||||||
|
t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
||||||
"format": stdlib.FormatFunc,
|
"format": stdlib.FormatFunc,
|
||||||
"formatlist": stdlib.FormatListFunc,
|
"formatlist": stdlib.FormatListFunc,
|
||||||
"indent": unimplFunc, // TODO
|
"indent": unimplFunc, // TODO
|
||||||
"index": unimplFunc, // TODO
|
"index": funcs.IndentFunc,
|
||||||
"join": funcs.JoinFunc,
|
"join": funcs.JoinFunc,
|
||||||
"jsondecode": stdlib.JSONDecodeFunc,
|
"jsondecode": stdlib.JSONDecodeFunc,
|
||||||
"jsonencode": stdlib.JSONEncodeFunc,
|
"jsonencode": stdlib.JSONEncodeFunc,
|
||||||
|
@ -77,7 +77,7 @@ func (s *Scope) Functions() map[string]function.Function {
|
||||||
"min": stdlib.MinFunc,
|
"min": stdlib.MinFunc,
|
||||||
"pathexpand": funcs.PathExpandFunc,
|
"pathexpand": funcs.PathExpandFunc,
|
||||||
"pow": funcs.PowFunc,
|
"pow": funcs.PowFunc,
|
||||||
"replace": unimplFunc, // TODO
|
"replace": funcs.ReplaceFunc,
|
||||||
"rsadecrypt": funcs.RsaDecryptFunc,
|
"rsadecrypt": funcs.RsaDecryptFunc,
|
||||||
"sha1": funcs.Sha1Func,
|
"sha1": funcs.Sha1Func,
|
||||||
"sha256": funcs.Sha256Func,
|
"sha256": funcs.Sha256Func,
|
||||||
|
@ -89,9 +89,9 @@ func (s *Scope) Functions() map[string]function.Function {
|
||||||
"substr": stdlib.SubstrFunc,
|
"substr": stdlib.SubstrFunc,
|
||||||
"timestamp": funcs.TimestampFunc,
|
"timestamp": funcs.TimestampFunc,
|
||||||
"timeadd": funcs.TimeAddFunc,
|
"timeadd": funcs.TimeAddFunc,
|
||||||
"title": unimplFunc, // TODO
|
"title": funcs.TitleFunc,
|
||||||
"transpose": unimplFunc, // TODO
|
"transpose": unimplFunc, // TODO
|
||||||
"trimspace": unimplFunc, // TODO
|
"trimspace": funcs.TrimSpace,
|
||||||
"upper": stdlib.UpperFunc,
|
"upper": stdlib.UpperFunc,
|
||||||
"urlencode": funcs.URLEncodeFunc,
|
"urlencode": funcs.URLEncodeFunc,
|
||||||
"uuid": funcs.UUIDFunc,
|
"uuid": funcs.UUIDFunc,
|
||||||
|
|
Loading…
Reference in New Issue