github.com/terraform-linters/tflint@v0.51.2-0.20240520175844-3750771571b6/terraform/lang/funcs/number.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package funcs 5 6 import ( 7 "math" 8 "math/big" 9 10 "github.com/zclconf/go-cty/cty" 11 "github.com/zclconf/go-cty/cty/function" 12 "github.com/zclconf/go-cty/cty/gocty" 13 ) 14 15 // LogFunc contructs a function that returns the logarithm of a given number in a given base. 16 var LogFunc = function.New(&function.Spec{ 17 Params: []function.Parameter{ 18 { 19 Name: "num", 20 Type: cty.Number, 21 }, 22 { 23 Name: "base", 24 Type: cty.Number, 25 }, 26 }, 27 Type: function.StaticReturnType(cty.Number), 28 RefineResult: refineNotNull, 29 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 30 var num float64 31 if err := gocty.FromCtyValue(args[0], &num); err != nil { 32 return cty.UnknownVal(cty.String), err 33 } 34 35 var base float64 36 if err := gocty.FromCtyValue(args[1], &base); err != nil { 37 return cty.UnknownVal(cty.String), err 38 } 39 40 return cty.NumberFloatVal(math.Log(num) / math.Log(base)), nil 41 }, 42 }) 43 44 // PowFunc contructs a function that returns the logarithm of a given number in a given base. 45 var PowFunc = function.New(&function.Spec{ 46 Params: []function.Parameter{ 47 { 48 Name: "num", 49 Type: cty.Number, 50 }, 51 { 52 Name: "power", 53 Type: cty.Number, 54 }, 55 }, 56 Type: function.StaticReturnType(cty.Number), 57 RefineResult: refineNotNull, 58 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 59 var num float64 60 if err := gocty.FromCtyValue(args[0], &num); err != nil { 61 return cty.UnknownVal(cty.String), err 62 } 63 64 var power float64 65 if err := gocty.FromCtyValue(args[1], &power); err != nil { 66 return cty.UnknownVal(cty.String), err 67 } 68 69 return cty.NumberFloatVal(math.Pow(num, power)), nil 70 }, 71 }) 72 73 // SignumFunc contructs a function that returns the closest whole number greater 74 // than or equal to the given value. 75 var SignumFunc = function.New(&function.Spec{ 76 Params: []function.Parameter{ 77 { 78 Name: "num", 79 Type: cty.Number, 80 }, 81 }, 82 Type: function.StaticReturnType(cty.Number), 83 RefineResult: refineNotNull, 84 Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) { 85 var num int 86 if err := gocty.FromCtyValue(args[0], &num); err != nil { 87 return cty.UnknownVal(cty.String), err 88 } 89 switch { 90 case num < 0: 91 return cty.NumberIntVal(-1), nil 92 case num > 0: 93 return cty.NumberIntVal(+1), nil 94 default: 95 return cty.NumberIntVal(0), nil 96 } 97 }, 98 }) 99 100 // ParseIntFunc contructs a function that parses a string argument and returns an integer of the specified base. 101 var ParseIntFunc = function.New(&function.Spec{ 102 Params: []function.Parameter{ 103 { 104 Name: "number", 105 Type: cty.DynamicPseudoType, 106 AllowMarked: true, 107 }, 108 { 109 Name: "base", 110 Type: cty.Number, 111 AllowMarked: true, 112 }, 113 }, 114 115 Type: func(args []cty.Value) (cty.Type, error) { 116 if !args[0].Type().Equals(cty.String) { 117 return cty.Number, function.NewArgErrorf(0, "first argument must be a string, not %s", args[0].Type().FriendlyName()) 118 } 119 return cty.Number, nil 120 }, 121 RefineResult: refineNotNull, 122 123 Impl: func(args []cty.Value, retType cty.Type) (cty.Value, error) { 124 var numstr string 125 var base int 126 var err error 127 128 numArg, numMarks := args[0].Unmark() 129 if err = gocty.FromCtyValue(numArg, &numstr); err != nil { 130 return cty.UnknownVal(cty.String), function.NewArgError(0, err) 131 } 132 133 baseArg, baseMarks := args[1].Unmark() 134 if err = gocty.FromCtyValue(baseArg, &base); err != nil { 135 return cty.UnknownVal(cty.Number), function.NewArgError(1, err) 136 } 137 138 if base < 2 || base > 62 { 139 return cty.UnknownVal(cty.Number), function.NewArgErrorf( 140 1, 141 "base must be a whole number between 2 and 62 inclusive", 142 ) 143 } 144 145 num, ok := (&big.Int{}).SetString(numstr, base) 146 if !ok { 147 return cty.UnknownVal(cty.Number), function.NewArgErrorf( 148 0, 149 "cannot parse %s as a base %s integer", 150 redactIfSensitive(numstr, numMarks), 151 redactIfSensitive(base, baseMarks), 152 ) 153 } 154 155 parsedNum := cty.NumberVal((&big.Float{}).SetInt(num)).WithMarks(numMarks, baseMarks) 156 157 return parsedNum, nil 158 }, 159 }) 160 161 // Log returns returns the logarithm of a given number in a given base. 162 func Log(num, base cty.Value) (cty.Value, error) { 163 return LogFunc.Call([]cty.Value{num, base}) 164 } 165 166 // Pow returns the logarithm of a given number in a given base. 167 func Pow(num, power cty.Value) (cty.Value, error) { 168 return PowFunc.Call([]cty.Value{num, power}) 169 } 170 171 // Signum determines the sign of a number, returning a number between -1 and 172 // 1 to represent the sign. 173 func Signum(num cty.Value) (cty.Value, error) { 174 return SignumFunc.Call([]cty.Value{num}) 175 } 176 177 // ParseInt parses a string argument and returns an integer of the specified base. 178 func ParseInt(num cty.Value, base cty.Value) (cty.Value, error) { 179 return ParseIntFunc.Call([]cty.Value{num, base}) 180 }