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:
parent
ebb551b86f
commit
096b1bb87b
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
]
|
||||
```
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue