diff --git a/hcl2template/function/anytrue.go b/hcl2template/function/anytrue.go new file mode 100644 index 00000000000..cfbef195e5b --- /dev/null +++ b/hcl2template/function/anytrue.go @@ -0,0 +1,41 @@ +package function + +import ( + "github.com/zclconf/go-cty/cty" + "github.com/zclconf/go-cty/cty/function" +) + +// AnyTrue constructs a function that returns true if a single element of +// the list is true. If the list is empty, return false. +var AnyTrue = function.New(&function.Spec{ + Params: []function.Parameter{ + { + Name: "list", + Type: cty.List(cty.Bool), + }, + }, + Type: function.StaticReturnType(cty.Bool), + RefineResult: refineNotNull, + Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { + result := cty.False + var hasUnknown bool + for it := args[0].ElementIterator(); it.Next(); { + _, v := it.Element() + if !v.IsKnown() { + hasUnknown = true + continue + } + if v.IsNull() { + continue + } + result = result.Or(v) + if result.True() { + return cty.True, nil + } + } + if hasUnknown { + return cty.UnknownVal(cty.Bool), nil + } + return result, nil + }, +}) diff --git a/hcl2template/function/anytrue_test.go b/hcl2template/function/anytrue_test.go new file mode 100644 index 00000000000..0ab830a89d3 --- /dev/null +++ b/hcl2template/function/anytrue_test.go @@ -0,0 +1,89 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: BUSL-1.1 + +package function + +import ( + "fmt" + "testing" + + "github.com/zclconf/go-cty/cty" +) + +func TestAnyTrue(t *testing.T) { + tests := []struct { + Collection cty.Value + Want cty.Value + Err bool + }{ + { + cty.ListValEmpty(cty.Bool), + cty.False, + false, + }, + { + cty.ListVal([]cty.Value{cty.True}), + cty.True, + false, + }, + { + cty.ListVal([]cty.Value{cty.False}), + cty.False, + false, + }, + { + cty.ListVal([]cty.Value{cty.True, cty.False}), + cty.True, + false, + }, + { + cty.ListVal([]cty.Value{cty.False, cty.True}), + cty.True, + false, + }, + { + cty.ListVal([]cty.Value{cty.True, cty.NullVal(cty.Bool)}), + cty.True, + false, + }, + { + cty.ListVal([]cty.Value{cty.UnknownVal(cty.Bool)}), + cty.UnknownVal(cty.Bool).RefineNotNull(), + false, + }, + { + cty.ListVal([]cty.Value{ + cty.UnknownVal(cty.Bool), + cty.UnknownVal(cty.Bool), + }), + cty.UnknownVal(cty.Bool).RefineNotNull(), + false, + }, + { + cty.UnknownVal(cty.List(cty.Bool)), + cty.UnknownVal(cty.Bool).RefineNotNull(), + false, + }, + { + cty.NullVal(cty.List(cty.Bool)), + cty.NilVal, + true, + }, + } + + for _, tc := range tests { + t.Run(fmt.Sprintf("anytrue(%#v)", tc.Collection), func(t *testing.T) { + got, err := AnyTrue.Call([]cty.Value{tc.Collection}) + + if tc.Err && err == nil { + t.Fatal("succeeded; want error") + } + if !tc.Err && err != nil { + t.Fatalf("unexpected error: %s", err) + } + if !got.RawEquals(tc.Want) { + t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, tc.Want) + } + }) + } +} diff --git a/hcl2template/functions.go b/hcl2template/functions.go index 6a2dbc61a42..4719a15a24b 100644 --- a/hcl2template/functions.go +++ b/hcl2template/functions.go @@ -35,6 +35,7 @@ func Functions(basedir string) map[string]function.Function { "abs": stdlib.AbsoluteFunc, "abspath": filesystem.AbsPathFunc, "alltrue": pkrfunction.AllTrue, + "anytrue": pkrfunction.AnyTrue, "aws_secretsmanager": pkrfunction.AWSSecret, "basename": filesystem.BasenameFunc, "base64decode": encoding.Base64DecodeFunc, diff --git a/website/content/docs/templates/hcl_templates/functions/collection/anytrue.mdx b/website/content/docs/templates/hcl_templates/functions/collection/anytrue.mdx new file mode 100644 index 00000000000..fb67069bd6c --- /dev/null +++ b/website/content/docs/templates/hcl_templates/functions/collection/anytrue.mdx @@ -0,0 +1,28 @@ +--- +page_title: anytrue - Functions - Configuration Language +description: |- + The anytrue function determines whether any element of a collection + is true or "true". If the collection is empty, it returns false. +--- + +# `anytrue` Function + +`anytrue` returns `true` if any element in a given collection is `true` +or `"true"`. It also returns `false` if the collection is empty. + +```hcl +anytrue(list) +``` + +## Examples + +```command +> anytrue(["true"]) +true +> anytrue([true]) +true +> anytrue([true, false]) +true +> anytrue([]) +false +``` diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index 55dfc244e30..51b173740f0 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -330,6 +330,10 @@ "title": "alltrue", "path": "templates/hcl_templates/functions/collection/alltrue" }, + { + "title": "anytrue", + "path": "templates/hcl_templates/functions/collection/anytrue" + }, { "title": "chunklist", "path": "templates/hcl_templates/functions/collection/chunklist"