porting functions

This commit is contained in:
Kristin Laemmert 2018-05-23 13:57:37 -07:00 committed by Martin Atkins
parent 4ad3676934
commit 602b59cdc4
6 changed files with 426 additions and 8 deletions

View File

@ -27,7 +27,129 @@ var CeilFunc = function.New(&function.Spec{
},
})
// FloorFunc contructs a function that returns the closest whole number lesser
// than or equal to the given value.
var FloorFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "num",
Type: cty.Number,
},
},
Type: function.StaticReturnType(cty.Number),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
var val float64
if err := gocty.FromCtyValue(args[0], &val); err != nil {
return cty.UnknownVal(cty.String), err
}
return cty.NumberIntVal(int64(math.Floor(val))), nil
},
})
// LogFunc contructs a function that returns the logarithm of a given number in a given base.
var LogFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "num",
Type: cty.Number,
},
{
Name: "base",
Type: cty.Number,
},
},
Type: function.StaticReturnType(cty.Number),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
var num float64
if err := gocty.FromCtyValue(args[0], &num); err != nil {
return cty.UnknownVal(cty.String), err
}
var base float64
if err := gocty.FromCtyValue(args[1], &base); err != nil {
return cty.UnknownVal(cty.String), err
}
return cty.NumberFloatVal(math.Log(num) / math.Log(base)), nil
},
})
// PowFunc contructs a function that returns the logarithm of a given number in a given base.
var PowFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "num",
Type: cty.Number,
},
{
Name: "power",
Type: cty.Number,
},
},
Type: function.StaticReturnType(cty.Number),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
var num float64
if err := gocty.FromCtyValue(args[0], &num); err != nil {
return cty.UnknownVal(cty.String), err
}
var power float64
if err := gocty.FromCtyValue(args[1], &power); err != nil {
return cty.UnknownVal(cty.String), err
}
return cty.NumberFloatVal(math.Pow(num, power)), nil
},
})
// SignumFunc contructs a function that returns the closest whole number greater
// than or equal to the given value.
var SignumFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "num",
Type: cty.Number,
},
},
Type: function.StaticReturnType(cty.Number),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
var num int
if err := gocty.FromCtyValue(args[0], &num); err != nil {
return cty.UnknownVal(cty.String), err
}
switch {
case num < 0:
return cty.NumberIntVal(-1), nil
case num > 0:
return cty.NumberIntVal(+1), nil
default:
return cty.NumberIntVal(0), nil
}
},
})
// Ceil returns the closest whole number greater than or equal to the given value.
func Ceil(num cty.Value) (cty.Value, error) {
return CeilFunc.Call([]cty.Value{num})
}
// Floor returns the closest whole number lesser than or equal to the given value.
func Floor(num cty.Value) (cty.Value, error) {
return FloorFunc.Call([]cty.Value{num})
}
// Log returns returns the logarithm of a given number in a given base.
func Log(num, base cty.Value) (cty.Value, error) {
return LogFunc.Call([]cty.Value{num, base})
}
// Pow returns the logarithm of a given number in a given base.
func Pow(num, power cty.Value) (cty.Value, error) {
return PowFunc.Call([]cty.Value{num, power})
}
// Signum determines the sign of a number, returning a number between -1 and
// 1 to represent the sign.
func Signum(num cty.Value) (cty.Value, error) {
return SignumFunc.Call([]cty.Value{num})
}

View File

@ -26,7 +26,7 @@ func TestCeil(t *testing.T) {
}
for _, test := range tests {
t.Run(fmt.Sprintf("Ceil(%#v)", test.Num), func(t *testing.T) {
t.Run(fmt.Sprintf("ceil(%#v)", test.Num), func(t *testing.T) {
got, err := Ceil(test.Num)
if test.Err {
@ -44,3 +44,216 @@ func TestCeil(t *testing.T) {
})
}
}
func TestFloor(t *testing.T) {
tests := []struct {
Num cty.Value
Want cty.Value
Err bool
}{
{
cty.NumberFloatVal(-1.8),
cty.NumberFloatVal(-2),
false,
},
{
cty.NumberFloatVal(1.2),
cty.NumberFloatVal(1),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("floor(%#v)", test.Num), func(t *testing.T) {
got, err := Floor(test.Num)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
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 TestLog(t *testing.T) {
tests := []struct {
Num cty.Value
Base cty.Value
Want cty.Value
Err bool
}{
{
cty.NumberFloatVal(1),
cty.NumberFloatVal(10),
cty.NumberFloatVal(0),
false,
},
{
cty.NumberFloatVal(10),
cty.NumberFloatVal(10),
cty.NumberFloatVal(1),
false,
},
{
cty.NumberFloatVal(0),
cty.NumberFloatVal(10),
cty.NegativeInfinity,
false,
},
{
cty.NumberFloatVal(10),
cty.NumberFloatVal(0),
cty.NumberFloatVal(-0),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("log(%#v, %#v)", test.Num, test.Base), func(t *testing.T) {
got, err := Log(test.Num, test.Base)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
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 TestPow(t *testing.T) {
tests := []struct {
Num cty.Value
Power cty.Value
Want cty.Value
Err bool
}{
{
cty.NumberFloatVal(1),
cty.NumberFloatVal(0),
cty.NumberFloatVal(1),
false,
},
{
cty.NumberFloatVal(1),
cty.NumberFloatVal(1),
cty.NumberFloatVal(1),
false,
},
{
cty.NumberFloatVal(2),
cty.NumberFloatVal(0),
cty.NumberFloatVal(1),
false,
},
{
cty.NumberFloatVal(2),
cty.NumberFloatVal(1),
cty.NumberFloatVal(2),
false,
},
{
cty.NumberFloatVal(3),
cty.NumberFloatVal(2),
cty.NumberFloatVal(9),
false,
},
{
cty.NumberFloatVal(-3),
cty.NumberFloatVal(2),
cty.NumberFloatVal(9),
false,
},
{
cty.NumberFloatVal(2),
cty.NumberFloatVal(-2),
cty.NumberFloatVal(0.25),
false,
},
{
cty.NumberFloatVal(0),
cty.NumberFloatVal(2),
cty.NumberFloatVal(0),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("pow(%#v, %#v)", test.Num, test.Power), func(t *testing.T) {
got, err := Pow(test.Num, test.Power)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
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 TestSignum(t *testing.T) {
tests := []struct {
Num cty.Value
Want cty.Value
Err bool
}{
{
cty.NumberFloatVal(0),
cty.NumberFloatVal(0),
false,
},
{
cty.NumberFloatVal(12),
cty.NumberFloatVal(1),
false,
},
{
cty.NumberFloatVal(-29),
cty.NumberFloatVal(-1),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("signum(%#v)", test.Num), func(t *testing.T) {
got, err := Signum(test.Num)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
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)
}
})
}
}

View File

@ -2,6 +2,7 @@ package funcs
import (
"fmt"
"regexp"
"sort"
"strings"
@ -110,6 +111,21 @@ var SplitFunc = function.New(&function.Spec{
},
})
// ChompFunc constructions a function that removes newline characters at the end of a string.
var ChompFunc = function.New(&function.Spec{
Params: []function.Parameter{
{
Name: "str",
Type: cty.String,
},
},
Type: function.StaticReturnType(cty.String),
Impl: func(args []cty.Value, retType cty.Type) (ret cty.Value, err error) {
newlines := regexp.MustCompile(`(?:\r\n?|\n)*\z`)
return cty.StringVal(newlines.ReplaceAllString(args[0].AsString(), "")), nil
},
})
// Join concatenates together the string elements of one or more lists with a
// given separator.
func Join(sep cty.Value, lists ...cty.Value) (cty.Value, error) {
@ -130,3 +146,8 @@ func Sort(list cty.Value) (cty.Value, error) {
func Split(sep, str cty.Value) (cty.Value, error) {
return SplitFunc.Call([]cty.Value{sep, str})
}
// Chomp removes newline characters at the end of a string.
func Chomp(str cty.Value) (cty.Value, error) {
return ChompFunc.Call([]cty.Value{str})
}

View File

@ -244,3 +244,66 @@ func TestSplit(t *testing.T) {
})
}
}
func TestChomp(t *testing.T) {
tests := []struct {
String cty.Value
Want cty.Value
Err bool
}{
{
cty.StringVal("hello world"),
cty.StringVal("hello world"),
false,
},
{
cty.StringVal("goodbye\ncruel\nworld"),
cty.StringVal("goodbye\ncruel\nworld"),
false,
},
{
cty.StringVal("goodbye\r\nwindows\r\nworld"),
cty.StringVal("goodbye\r\nwindows\r\nworld"),
false,
},
{
cty.StringVal("goodbye\ncruel\nworld\n"),
cty.StringVal("goodbye\ncruel\nworld"),
false,
},
{
cty.StringVal("goodbye\ncruel\nworld\n\n\n\n"),
cty.StringVal("goodbye\ncruel\nworld"),
false,
},
{
cty.StringVal("goodbye\r\nwindows\r\nworld\r\n"),
cty.StringVal("goodbye\r\nwindows\r\nworld"),
false,
},
{
cty.StringVal("goodbye\r\nwindows\r\nworld\r\n\r\n\r\n\r\n"),
cty.StringVal("goodbye\r\nwindows\r\nworld"),
false,
},
}
for _, test := range tests {
t.Run(fmt.Sprintf("chomp(%#v)", test.String), func(t *testing.T) {
got, err := Chomp(test.String)
if test.Err {
if err == nil {
t.Fatal("succeeded; want error")
}
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)
}
})
}
}

View File

@ -38,7 +38,7 @@ func (s *Scope) Functions() map[string]function.Function {
"base64sha512": funcs.Base64Sha512Func,
"bcrypt": funcs.BcryptFunc,
"ceil": funcs.CeilFunc,
"chomp": unimplFunc, // TODO
"chomp": funcs.ChompFunc,
"cidrhost": unimplFunc, // TODO
"cidrnetmask": unimplFunc, // TODO
"cidrsubnet": unimplFunc, // TODO
@ -56,7 +56,7 @@ func (s *Scope) Functions() map[string]function.Function {
"filebase64": funcs.MakeFileFunc(s.BaseDir, true),
"matchkeys": unimplFunc, // TODO
"flatten": unimplFunc, // TODO
"floor": unimplFunc, // TODO
"floor": funcs.FloorFunc,
"format": stdlib.FormatFunc,
"formatlist": stdlib.FormatListFunc,
"indent": unimplFunc, // TODO
@ -67,7 +67,7 @@ func (s *Scope) Functions() map[string]function.Function {
"keys": unimplFunc, // TODO
"length": funcs.LengthFunc,
"list": unimplFunc, // TODO
"log": unimplFunc, // TODO
"log": funcs.LogFunc,
"lookup": unimplFunc, // TODO
"lower": stdlib.LowerFunc,
"map": unimplFunc, // TODO
@ -76,7 +76,7 @@ func (s *Scope) Functions() map[string]function.Function {
"merge": unimplFunc, // TODO
"min": stdlib.MinFunc,
"pathexpand": funcs.PathExpandFunc,
"pow": unimplFunc, // TODO
"pow": funcs.PowFunc,
"replace": unimplFunc, // TODO
"rsadecrypt": funcs.RsaDecryptFunc,
"sha1": funcs.Sha1Func,

View File

@ -3,13 +3,12 @@ layout: "functions"
page_title: "log function"
sidebar_current: "docs-funcs-numeric-log"
description: |-
The log function returns the closest whole number less than or equal to
the given value.
The log function returns the logarithm of a given number in a given base.
---
# `log` Function
`log` returns the the logarithm of a given number in a given base.
`log` returns the logarithm of a given number in a given base.
```hcl
log(number, base)