lang/funcs: Port the "reverse" function from the old functions set

This has the same functionality as the "reverse" function that was
implemented in the "config" package, but adapted to the new language type
system.
This commit is contained in:
Martin Atkins 2019-03-19 17:09:01 -07:00
parent ebb551b86f
commit 096b1bb87b
5 changed files with 203 additions and 0 deletions

View File

@ -807,6 +807,49 @@ var MergeFunc = function.New(&function.Spec{
},
})
// ReverseFunc takes a sequence and produces a new sequence of the same length
// with all of the same elements as the given sequence but in reverse order.
var ReverseFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "list",
Type: cty.DynamicPseudoType,
},
},
Type: func(args []cty.Value) (cty.Type, error) {
argTy := args[0].Type()
switch {
case argTy.IsTupleType():
argTys := argTy.TupleElementTypes()
retTys := make([]cty.Type, len(argTys))
for i, ty := range argTys {
retTys[len(retTys)-i-1] = ty
}
return cty.Tuple(retTys), nil
case argTy.IsListType(), argTy.IsSetType(): // We accept sets here to mimic the usual behavior of auto-converting to list
return cty.List(argTy.ElementType()), nil
default:
return cty.NilType, function.NewArgErrorf(0, "can only reverse list or tuple values, not %s", argTy.FriendlyName())
}
},
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
in := args[0].AsValueSlice()
outVals := make([]cty.Value, len(in))
for i, v := range in {
outVals[len(outVals)-i-1] = v
}
switch {
case retType.IsTupleType():
return cty.TupleVal(outVals), nil
default:
if len(outVals) == 0 {
return cty.ListValEmpty(retType.ElementType()), nil
}
return cty.ListVal(outVals), nil
}
},
})
// SetProductFunc calculates the cartesian product of two or more sets or
// sequences. If the arguments are all lists then the result is a list of tuples,
// preserving the ordering of all of the input lists. Otherwise the result is a
@ -1292,6 +1335,12 @@ func Merge(maps ...cty.Value) (cty.Value, error) {
return MergeFunc.Call(maps)
}
// Reverse takes a sequence and produces a new sequence of the same length
// with all of the same elements as the given sequence but in reverse order.
func Reverse(list cty.Value) (cty.Value, error) {
return ReverseFunc.Call([]cty.Value{list})
}
// SetProduct computes the cartesian product of sets or sequences.
func SetProduct(sets ...cty.Value) (cty.Value, error) {
return SetProductFunc.Call(sets)

View File

@ -1854,6 +1854,128 @@ func TestMerge(t *testing.T) {
}
}
func TestReverse(t *testing.T) {
tests := []struct {
List cty.Value
Want cty.Value
Err string
}{
{
cty.ListValEmpty(cty.String),
cty.ListValEmpty(cty.String),
"",
},
{
cty.ListVal([]cty.Value{cty.StringVal("a")}),
cty.ListVal([]cty.Value{cty.StringVal("a")}),
"",
},
{
cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}),
cty.ListVal([]cty.Value{cty.StringVal("b"), cty.StringVal("a")}),
"",
},
{
cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b"), cty.StringVal("c")}),
cty.ListVal([]cty.Value{cty.StringVal("c"), cty.StringVal("b"), cty.StringVal("a")}),
"",
},
{
cty.ListVal([]cty.Value{cty.UnknownVal(cty.String), cty.StringVal("b"), cty.StringVal("c")}),
cty.ListVal([]cty.Value{cty.StringVal("c"), cty.StringVal("b"), cty.UnknownVal(cty.String)}),
"",
},
{
cty.EmptyTupleVal,
cty.EmptyTupleVal,
"",
},
{
cty.TupleVal([]cty.Value{cty.StringVal("a")}),
cty.TupleVal([]cty.Value{cty.StringVal("a")}),
"",
},
{
cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.True}),
cty.TupleVal([]cty.Value{cty.True, cty.StringVal("a")}),
"",
},
{
cty.TupleVal([]cty.Value{cty.StringVal("a"), cty.True, cty.Zero}),
cty.TupleVal([]cty.Value{cty.Zero, cty.True, cty.StringVal("a")}),
"",
},
{
cty.SetValEmpty(cty.String),
cty.ListValEmpty(cty.String),
"",
},
{
cty.SetVal([]cty.Value{cty.StringVal("a")}),
cty.ListVal([]cty.Value{cty.StringVal("a")}),
"",
},
{
cty.SetVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}),
cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b")}),
"",
},
{
cty.SetVal([]cty.Value{cty.StringVal("a"), cty.StringVal("b"), cty.StringVal("c")}),
cty.ListVal([]cty.Value{cty.StringVal("a"), cty.StringVal("c"), cty.StringVal("b")}),
"",
},
{
cty.StringVal("no"),
cty.NilVal,
"can only reverse list or tuple values, not string",
},
{
cty.True,
cty.NilVal,
"can only reverse list or tuple values, not bool",
},
{
cty.MapValEmpty(cty.String),
cty.NilVal,
"can only reverse list or tuple values, not map of string",
},
{
cty.NullVal(cty.List(cty.String)),
cty.NilVal,
"argument must not be null",
},
{
cty.UnknownVal(cty.List(cty.String)),
cty.UnknownVal(cty.List(cty.String)),
"",
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("reverse(%#v)", test.List), func(t *testing.T) {
got, err := Reverse(test.List)
if test.Err != "" {
if err == nil {
t.Fatal("succeeded; want error")
}
if got, want := err.Error(), test.Err; got != want {
t.Fatalf("wrong error\ngot: %s\nwant: %s", got, want)
}
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 TestSetProduct(t *testing.T) {
tests := []struct {
Sets []cty.Value

View File

@ -86,6 +86,7 @@ func (s *Scope) Functions() map[string]function.Function {
"pathexpand": funcs.PathExpandFunc,
"pow": funcs.PowFunc,
"replace": funcs.ReplaceFunc,
"reverse": funcs.ReverseFunc,
"rsadecrypt": funcs.RsaDecryptFunc,
"sethaselement": stdlib.SetHasElementFunc,
"setintersection": stdlib.SetIntersectionFunc,

View File

@ -0,0 +1,27 @@
---
layout: "functions"
page_title: "reverse - Functions - Configuration Language"
sidebar_current: "docs-funcs-collection-reverse"
description: |-
The reverse function reverses a sequence.
---
# `reverse` Function
-> **Note:** This page is about Terraform 0.12 and later. For Terraform 0.11 and
earlier, see
[0.11 Configuration Language: Interpolation Syntax](../../configuration-0-11/interpolation.html).
`reverse` takes a sequence and produces a new sequence of the same length
with all of the same elements as the given sequence but in reverse order.
## Examples
```
> reverse([1, 2, 3])
[
3,
2,
1,
]
```

View File

@ -171,6 +171,10 @@
<a href="/docs/configuration/functions/merge.html">merge</a>
</li>
<li<%= sidebar_current("docs-funcs-collection-reverse") %>>
<a href="/docs/configuration/functions/reverse.html">reverse</a>
</li>
<li<%= sidebar_current("docs-funcs-collection-sethaselement") %>>
<a href="/docs/configuration/functions/sethaselement.html">sethaselement</a>
</li>