Skip to content

Commit

Permalink
chore(perf): optimise tm_alltrue() and tm_anytrue().
Browse files Browse the repository at this point in the history
Signed-off-by: Tiago Natel <[email protected]>
  • Loading branch information
i4ki committed Dec 6, 2023
1 parent 121a035 commit fd2a9b0
Show file tree
Hide file tree
Showing 3 changed files with 278 additions and 0 deletions.
129 changes: 129 additions & 0 deletions globals/globals_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2870,6 +2870,135 @@ func TestLoadGlobals(t *testing.T) {
),
},
},
{
name: "tm_alltrue with literal list of bool",
layout: []string{"s:stack"},
configs: []hclconfig{
{
path: "/stack",
add: Globals(
Expr("val", `tm_alltrue([true, 1==1, false==false, !false])`),
),
},
},
want: map[string]*hclwrite.Block{
"/stack": Globals(
EvalExpr(t, "val", `true`),
),
},
},
{
name: "tm_alltrue with literal tuple containing non-boolean",
layout: []string{"s:stack"},
configs: []hclconfig{
{
path: "/stack",
add: Globals(
Expr("val", `tm_alltrue([true, 1==1, "string", {}])`),
),
},
},
wantErr: errors.E(globals.ErrEval),
},
{
name: "tm_alltrue with literal for-loop",
layout: []string{"s:stack"},
configs: []hclconfig{
{
path: "/stack",
add: Globals(
Expr("val", `tm_alltrue([for i in [true, 1==1, !false] : i])`),
),
},
},
want: map[string]*hclwrite.Block{
"/stack": Globals(
EvalExpr(t, "val", `true`),
),
},
},
{
name: "tm_alltrue with funcall",
layout: []string{"s:stack"},
configs: []hclconfig{
{
path: "/stack",
add: Globals(
Expr("val", `tm_alltrue(tm_distinct([true, !false, 1==1]))`),
),
},
},
want: map[string]*hclwrite.Block{
"/stack": Globals(
EvalExpr(t, "val", `true`),
),
},
},

{
name: "tm_anytrue with literal list of bool",
layout: []string{"s:stack"},
configs: []hclconfig{
{
path: "/stack",
add: Globals(
Expr("val", `tm_anytrue([false, 1!=1, false==false, !false])`),
),
},
},
want: map[string]*hclwrite.Block{
"/stack": Globals(
EvalExpr(t, "val", `true`),
),
},
},
{
name: "tm_anytrue with literal tuple containing non-boolean",
layout: []string{"s:stack"},
configs: []hclconfig{
{
path: "/stack",
add: Globals(
Expr("val", `tm_anytrue([false, 1!=1, "string", {}])`),
),
},
},
wantErr: errors.E(globals.ErrEval),
},
{
name: "tm_anytrue with literal for-loop",
layout: []string{"s:stack"},
configs: []hclconfig{
{
path: "/stack",
add: Globals(
Expr("val", `tm_anytrue([for i in [false, 1!=1, !false] : i])`),
),
},
},
want: map[string]*hclwrite.Block{
"/stack": Globals(
EvalExpr(t, "val", `true`),
),
},
},
{
name: "tm_anytrue with funcall",
layout: []string{"s:stack"},
configs: []hclconfig{
{
path: "/stack",
add: Globals(
Expr("val", `tm_anytrue(tm_distinct([false, false, 1==1]))`),
),
},
},
want: map[string]*hclwrite.Block{
"/stack": Globals(
EvalExpr(t, "val", `true`),
),
},
},
{
name: "globals.map label conflicts with global name",
layout: []string{"s:stack"},
Expand Down
145 changes: 145 additions & 0 deletions stdlib/collection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright 2023 Terramate GmbH
// SPDX-License-Identifier: MPL-2.0

package stdlib

import (
"github.com/hashicorp/hcl/v2/ext/customdecode"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/terramate-io/terramate/errors"
"github.com/terramate-io/terramate/hcl/eval"
"github.com/zclconf/go-cty/cty"
"github.com/zclconf/go-cty/cty/function"
)

// AllTrueFunc implements the `tm_alltrue()` function.
func AllTrueFunc() function.Function {
return function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "list",
Type: customdecode.ExpressionClosureType,
},
},
Type: function.StaticReturnType(cty.Bool),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
argClosure := customdecode.ExpressionClosureFromVal(args[0])
evalctx := eval.NewContextFrom(argClosure.EvalContext)
listExpression, ok := argClosure.Expression.(*hclsyntax.TupleConsExpr)
if !ok {
v, err := evalctx.Eval(argClosure.Expression)
if err != nil {
return cty.False, err
}
if !v.Type().IsListType() && !v.Type().IsTupleType() {
return cty.False, errors.E(`Invalid value for "list" parameter: %s`, v.Type().FriendlyName())
}
result := true
i := 0
for it := v.ElementIterator(); it.Next(); {
_, v := it.Element()
if !v.IsKnown() {
return cty.UnknownVal(cty.Bool), nil
}
if v.IsNull() {
return cty.False, nil
}
if !v.Type().Equals(cty.Bool) {
return cty.False, errors.E(`Invalid value for "list" parameter: element %d: bool required`, i+1)
}
result = result && v.True()
if !result {
return cty.False, nil
}
i++
}
return cty.True, nil
}

result := true
for i, expr := range listExpression.Exprs {
v, err := evalctx.Eval(expr)
if err != nil {
return cty.False, err
}
if v.IsNull() {
return cty.False, nil
}
if !v.Type().Equals(cty.Bool) {
return cty.False, errors.E(`Invalid value for "list" parameter: element %d: bool required`, i+1)
}
result = result && v.True()
if !result {
return cty.False, nil
}
}
return cty.True, nil
},
})
}

// AnyTrueFunc implements the `tm_anytrue()` function.
func AnyTrueFunc() function.Function {
return function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "list",
Type: customdecode.ExpressionClosureType,
},
},
Type: function.StaticReturnType(cty.Bool),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
argClosure := customdecode.ExpressionClosureFromVal(args[0])
evalctx := eval.NewContextFrom(argClosure.EvalContext)
listExpression, ok := argClosure.Expression.(*hclsyntax.TupleConsExpr)
if !ok {
v, err := evalctx.Eval(argClosure.Expression)
if err != nil {
return cty.False, err
}
if !v.Type().IsListType() && !v.Type().IsTupleType() {
return cty.False, errors.E(`Invalid value for "list" parameter: %s`, v.Type().FriendlyName())
}
result := false
i := 0
for it := v.ElementIterator(); it.Next(); {
_, v := it.Element()
if !v.IsKnown() {
continue
}
if v.IsNull() {
continue
}
if !v.Type().Equals(cty.Bool) {
return cty.False, errors.E(`Invalid value for "list" parameter: element %d: bool required`, i+1)
}
result = result || v.True()
if result {
return cty.True, nil
}
i++
}
return cty.False, nil
}

result := false
for i, expr := range listExpression.Exprs {
v, err := evalctx.Eval(expr)
if err != nil {
return cty.False, err
}
if v.IsNull() {
continue
}
if !v.Type().Equals(cty.Bool) {
return cty.False, errors.E(`Invalid value for "list" parameter: element %d: bool required`, i+1)
}
result = result || v.True()
if result {
return cty.True, nil
}
}
return cty.False, nil
},
})
}
4 changes: 4 additions & 0 deletions stdlib/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ func Functions(basedir string) map[string]function.Function {
// sane ternary
tmfuncs["tm_ternary"] = TernaryFunc()

// optimized collection functions
tmfuncs["tm_alltrue"] = AllTrueFunc()
tmfuncs["tm_anytrue"] = AnyTrueFunc()

tmfuncs["tm_version_match"] = VersionMatch()
return tmfuncs
}
Expand Down

0 comments on commit fd2a9b0

Please sign in to comment.