github.com/hashicorp/packer@v1.14.3/hcl2template/function/sum.go (about)

     1  package function
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  
     7  	"github.com/zclconf/go-cty/cty"
     8  	"github.com/zclconf/go-cty/cty/convert"
     9  	"github.com/zclconf/go-cty/cty/function"
    10  )
    11  
    12  var SumFunc = function.New(&function.Spec{
    13  	Params: []function.Parameter{
    14  		{
    15  			Name: "list",
    16  			Type: cty.DynamicPseudoType,
    17  		},
    18  	},
    19  	Type:         function.StaticReturnType(cty.Number),
    20  	RefineResult: refineNotNull,
    21  	Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
    22  
    23  		if !args[0].CanIterateElements() {
    24  			return cty.NilVal, function.NewArgErrorf(0, "cannot sum noniterable")
    25  		}
    26  
    27  		if args[0].LengthInt() == 0 { // Easy path
    28  			return cty.NilVal, function.NewArgErrorf(0, "cannot sum an empty list")
    29  		}
    30  
    31  		arg := args[0].AsValueSlice()
    32  		ty := args[0].Type()
    33  
    34  		if !ty.IsListType() && !ty.IsSetType() && !ty.IsTupleType() {
    35  			return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple. Received %s", ty.FriendlyName())
    36  		}
    37  
    38  		if !args[0].IsWhollyKnown() {
    39  			return cty.UnknownVal(cty.Number), nil
    40  		}
    41  
    42  		// big.Float.Add can panic if the input values are opposing infinities,
    43  		// so we must catch that here in order to remain within
    44  		// the cty Function abstraction.
    45  		defer func() {
    46  			if r := recover(); r != nil {
    47  				if _, ok := r.(big.ErrNaN); ok {
    48  					ret = cty.NilVal
    49  					err = fmt.Errorf("can't compute sum of opposing infinities")
    50  				} else {
    51  					// not a panic we recognize
    52  					panic(r)
    53  				}
    54  			}
    55  		}()
    56  
    57  		s := arg[0]
    58  		if s.IsNull() {
    59  			return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values")
    60  		}
    61  		s, err = convert.Convert(s, cty.Number)
    62  		if err != nil {
    63  			return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values")
    64  		}
    65  		for _, v := range arg[1:] {
    66  			if v.IsNull() {
    67  				return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values")
    68  			}
    69  			v, err = convert.Convert(v, cty.Number)
    70  			if err != nil {
    71  				return cty.NilVal, function.NewArgErrorf(0, "argument must be list, set, or tuple of number values")
    72  			}
    73  			s = s.Add(v)
    74  		}
    75  
    76  		return s, nil
    77  	},
    78  })
    79  
    80  // Sum adds numbers in a list, set, or tuple
    81  func Sum(list cty.Value) (cty.Value, error) {
    82  	return SumFunc.Call([]cty.Value{list})
    83  }