diff --git a/pkg/expr/func_arr.go b/pkg/expr/func_arr.go new file mode 100644 index 000000000..fd90fed8e --- /dev/null +++ b/pkg/expr/func_arr.go @@ -0,0 +1,121 @@ +package expr + +import ( + "reflect" + + "github.com/PaesslerAG/gval" +) + +func ArrayFunctions() []gval.Language { + return []gval.Language{ + gval.Function("push", push), + gval.Function("pop", pop), + gval.Function("shift", shift), + gval.Function("count", count), + gval.Function("has", has), + gval.Function("hasAll", hasAll), + } +} + +// push adds a value to the end of slice, returns copy +func push(arr interface{}, p interface{}) (interface{}, error) { + if !isSlice(arr) { + return nil, &reflect.ValueError{Method: "Index", Kind: reflect.ValueOf(arr).Kind()} + } + + c := reflect.ValueOf(arr) + + nval := reflect.MakeSlice(c.Type(), c.Len()+1, c.Cap()+1) + reflect.Copy(nval, c) + nval.Index(c.Len()).Set(reflect.ValueOf(p)) + + return nval.Interface(), nil +} + +// pop takes the last value in slice, does not modify original +func pop(arr interface{}) (interface{}, error) { + if !isSlice(arr) { + return nil, &reflect.ValueError{Method: "Index", Kind: reflect.ValueOf(arr).Kind()} + } + + c := reflect.ValueOf(arr) + + if c.Len() == 0 { + return nil, nil + } + + return c.Index(c.Len() - 1).Interface(), nil +} + +// shifts takes the first value in slice, does not modify original +func shift(arr interface{}) (interface{}, error) { + if !isSlice(arr) { + return nil, &reflect.ValueError{Method: "Index", Kind: reflect.ValueOf(arr).Kind()} + } + + c := reflect.ValueOf(arr) + + if c.Len() == 0 { + return nil, nil + } + + return c.Index(0).Interface(), nil +} + +// cound gets the count of occurences in the slice +func count(arr interface{}, v ...interface{}) int { + if !isSlice(arr) { + return 0 + } + + count := 0 + + for _, vv := range v { + if find(arr, vv) != -1 { + count++ + } + } + + return count +} + +// has finds any occurence of the values in slice +func has(arr interface{}, v ...interface{}) bool { + return count(arr, v...) > 0 +} + +// hasAll finds all the occurences in the slice +func hasAll(arr interface{}, v ...interface{}) bool { return count(arr, v...) == len(v) } + +// find takes a value and gets the position in slice +// if no results, returns -1 +func find(arr interface{}, v interface{}) int { + if !isSlice(arr) { + return 0 + } + + for i := 0; i < reflect.ValueOf(arr).Len(); i++ { + c := reflect.ValueOf(arr) + + if c.Index(i).Interface() == v { + return i + } + } + + return -1 +} + +// slice slices slices +func slice(arr interface{}, start, end int) interface{} { + v := reflect.ValueOf(arr) + + if start >= v.Len() { + return v.Interface() + } + + if end == -1 || end > v.Len() { + end = v.Len() + } + + return v.Slice(start, end).Interface() +} diff --git a/pkg/expr/func_arr_test.go b/pkg/expr/func_arr_test.go new file mode 100644 index 000000000..1d4f28cec --- /dev/null +++ b/pkg/expr/func_arr_test.go @@ -0,0 +1,378 @@ +package expr + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/require" +) + +var ( + intArr = []int{} + stringArr = []string{"first"} + boolArr = []bool{true, true, false} + floatArr = []float64{69.420} + + vals = map[string]interface{}{ + "intArr": intArr, + "stringArr": stringArr, + "boolArr": boolArr, + "floatArr": floatArr, + "intVal": 42, + "stringVal": "foobar", + "boolVal": false, + } +) + +type ( + fac struct { + expect interface{} + arr interface{} + val interface{} + } +) + +func Example_push_int() { + eval(`push(intArr, intVal)`, vals) + + // output: + // [42] +} + +func Example_push_string() { + eval(`push(stringArr, stringVal)`, vals) + + // output: + // [first foobar] +} + +func Example_push_bool() { + eval(`push(boolArr, boolVal)`, vals) + + // output: + // [true true false false] +} + +func Example_push_float() { + eval(`push(floatArr, 3.14)`, vals) + + // output: + // [69.42 3.14] +} + +func Example_pop_string() { + eval(`pop(stringArr)`, vals) + + // output: + // first +} + +func Example_pop_int() { + eval(`pop(intArr)`, vals) + + // output: + // +} + +func Example_pop_float() { + eval(`pop(floatArr)`, vals) + + // output: + // 69.42 +} + +func Test_shift(t *testing.T) { + var ( + req = require.New(t) + + tcc = []tc{ + { + value: []string{"1", "2", "3"}, + expect: "1", + }, + { + value: map[string]string{"test": "123"}, + expect: nil, + }, + { + value: []int{4, 5, 6, 7}, + expect: 4, + }, + { + value: []int{}, + expect: nil, + }, + { + value: int(1), + expect: nil, + }, + { + value: []float64{11.1}, + expect: 11.1, + }, + } + ) + + for _, tst := range tcc { + val, _ := shift(tst.value) + + req.Equal(tst.expect, val) + } +} + +func Test_find(t *testing.T) { + var ( + req = require.New(t) + + tcc = []fac{ + { + arr: []string{"1", "2", "3"}, + val: "3", + expect: 2, + }, + { + arr: []bool{true, false, true}, + val: true, + expect: 0, + }, + { + arr: []int{4, 5, 6, 7}, + val: 7, + expect: 3, + }, + { + arr: []int{}, + val: 0, + expect: -1, + }, + { + arr: []float64{11.1, 12.4}, + val: 11.1, + expect: 0, + }, + { + arr: []float64{11.1, 12.4}, + val: 11.2, + expect: -1, + }, + } + ) + + for _, tst := range tcc { + loc := find(tst.arr, tst.val) + + req.Equal(tst.expect, loc) + } +} + +func Test_count(t *testing.T) { + var ( + req = require.New(t) + + tcc = []fac{ + { + arr: []string{"1", "2", "3"}, + val: []string{"0", "3"}, + expect: 1, + }, + { + arr: []bool{true, true}, + val: []bool{false, false}, + expect: 0, + }, + { + arr: []bool{true, true}, + val: []bool{false, true}, + expect: 1, + }, + { + arr: []int{4, 5, 6, 7}, + val: []int{7, 4}, + expect: 2, + }, + { + arr: []float64{11.1, 12.4}, + val: []float64{0.1, 1.1}, + expect: 0, + }, + } + ) + + for _, tst := range tcc { + var loc int + + switch reflect.TypeOf(tst.val).Elem().Kind() { + case reflect.String: + loc = count(tst.arr, tst.val.([]string)[0], tst.val.([]string)[1]) + break + case reflect.Bool: + loc = count(tst.arr, tst.val.([]bool)[0], tst.val.([]bool)[1]) + break + case reflect.Int: + loc = count(tst.arr, tst.val.([]int)[0], tst.val.([]int)[1]) + break + case reflect.Float64: + loc = count(tst.arr, tst.val.([]float64)[0], tst.val.([]float64)[1]) + break + } + + req.Equal(tst.expect, loc) + } +} + +func Test_has(t *testing.T) { + var ( + req = require.New(t) + + tcc = []fac{ + { + arr: []string{"1", "2", "3"}, + val: []string{"0", "3"}, + expect: true, + }, + { + arr: []bool{true, true}, + val: []bool{false, false}, + expect: false, + }, + { + arr: []bool{true, true}, + val: []bool{false, true}, + expect: true, + }, + { + arr: []int{4, 5, 6, 7}, + val: []int{7, 4}, + expect: true, + }, + { + arr: []float64{11.1, 12.4}, + val: []float64{0.1, 1.1}, + expect: false, + }, + } + ) + + for _, tst := range tcc { + var loc bool + + switch reflect.TypeOf(tst.val).Elem().Kind() { + case reflect.String: + loc = has(tst.arr, tst.val.([]string)[0], tst.val.([]string)[1]) + break + case reflect.Bool: + loc = has(tst.arr, tst.val.([]bool)[0], tst.val.([]bool)[1]) + break + case reflect.Int: + loc = has(tst.arr, tst.val.([]int)[0], tst.val.([]int)[1]) + break + case reflect.Float64: + loc = has(tst.arr, tst.val.([]float64)[0], tst.val.([]float64)[1]) + break + } + + req.Equal(tst.expect, loc) + } +} + +func Test_hasAll(t *testing.T) { + var ( + req = require.New(t) + + tcc = []fac{ + { + arr: []string{"1", "2", "3"}, + val: []string{"0", "3"}, + expect: false, + }, + { + arr: []bool{true, true}, + val: []bool{false, false}, + expect: false, + }, + { + arr: []bool{true, true}, + val: []bool{false, true}, + expect: false, + }, + { + arr: []int{4, 5, 6, 7}, + val: []int{7, 4}, + expect: true, + }, + { + arr: []float64{11.1, 12.4}, + val: []float64{0.1, 1.1}, + expect: false, + }, + } + ) + + for _, tst := range tcc { + var loc bool + + switch reflect.TypeOf(tst.arr).Elem().Kind() { + case reflect.String: + loc = hasAll(tst.arr, tst.val.([]string)[0], tst.val.([]string)[1]) + break + case reflect.Bool: + loc = hasAll(tst.arr, tst.val.([]bool)[0], tst.val.([]bool)[1]) + break + case reflect.Int: + loc = hasAll(tst.arr, tst.val.([]int)[0], tst.val.([]int)[1]) + break + case reflect.Float64: + loc = hasAll(tst.arr, tst.val.([]float64)[0], tst.val.([]float64)[1]) + break + } + + req.Equal(tst.expect, loc) + } +} + +func Test_slice(t *testing.T) { + type ( + sct struct { + vals []int + arr interface{} + expect interface{} + } + ) + + var ( + req = require.New(t) + + tcc = []sct{ + { + vals: []int{0, 3}, + arr: []string{"1", "2", "3"}, + expect: []string{"1", "2", "3"}, + }, + { + vals: []int{0, 1}, + arr: []string{"1", "2", "3"}, + expect: []string{"1"}, + }, + { + vals: []int{2, 3}, + arr: []bool{true, true}, + expect: []bool{true, true}, + }, + { + vals: []int{1, -1}, + arr: []int{4, 5, 6, 7}, + expect: []int{5, 6, 7}, + }, + { + vals: []int{3, -1}, + arr: []float64{11.1, 12.4}, + expect: []float64{11.1, 12.4}, + }, + } + ) + + for _, tst := range tcc { + new := slice(tst.arr, tst.vals[0], tst.vals[1]) + req.Equal(tst.expect, new) + } +} diff --git a/pkg/expr/func_gen.go b/pkg/expr/func_gen.go index 1f13e6972..768458642 100644 --- a/pkg/expr/func_gen.go +++ b/pkg/expr/func_gen.go @@ -1,13 +1,17 @@ package expr import ( - "github.com/PaesslerAG/gval" "reflect" + + "github.com/PaesslerAG/gval" + "github.com/davecgh/go-spew/spew" ) func GenericFunctions() []gval.Language { return []gval.Language{ gval.Function("coalesce", coalesce), + gval.Function("isEmpty", isEmpty), + gval.Function("isNil", isNil), } } @@ -32,3 +36,60 @@ func isNil(i interface{}) bool { return false } + +// empty checks values and slices +func isEmpty(i interface{}) bool { + if isNil(i) { + return true + } + + spew.Dump("A", reflect.ValueOf(i).IsZero()) + + switch reflect.TypeOf(i).Kind() { + case reflect.Slice, reflect.Array, reflect.Ptr, reflect.Map: + return reflect.ValueOf(i).Len() == 0 + } + + return reflect.ValueOf(i).IsZero() +} + +func isInt(v interface{}) bool { + switch getKind(v) { + case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int8, reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint8: + return true + } + + return false +} + +func isString(v interface{}) bool { + return getKind(v) == reflect.String +} + +func isFloat(v interface{}) bool { + switch getKind(v) { + case reflect.Float32, reflect.Float64: + return true + } + + return false +} + +func isBool(v interface{}) bool { + return getKind(v) == reflect.Bool +} + +func getKind(v interface{}) reflect.Kind { + kind := reflect.TypeOf(v).Kind() + + if isSlice(v) { + kind = reflect.TypeOf(v).Elem().Kind() + + } + + return kind +} + +func isSlice(v interface{}) bool { + return reflect.TypeOf(v).Kind() == reflect.Slice +} diff --git a/pkg/expr/func_gen_test.go b/pkg/expr/func_gen_test.go new file mode 100644 index 000000000..c8dcbdce8 --- /dev/null +++ b/pkg/expr/func_gen_test.go @@ -0,0 +1,93 @@ +package expr + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +type ( + tc struct { + value interface{} + expect interface{} + } +) + +func Test_empty(t *testing.T) { + var ( + req = require.New(t) + unsetSliceString []string + unsetSliceInt []int8 + unsetSliceBool []bool + unsetSliceFloat []float32 + unsetString string + unsetInt64 int64 + + tcc = []tc{ + { + value: []string{}, + expect: true, + }, + { + value: map[string]string{}, + expect: true, + }, + { + value: unsetSliceString, + expect: true, + }, + { + value: []int{}, + expect: true, + }, + { + value: []int{1}, + expect: false, + }, + { + value: unsetSliceInt, + expect: true, + }, + { + value: unsetSliceBool, + expect: true, + }, + { + value: int(1), + expect: false, + }, + { + value: int(0), + expect: true, + }, + { + value: "", + expect: true, + }, + { + value: unsetString, + expect: true, + }, + { + value: unsetSliceFloat, + expect: true, + }, + { + value: unsetInt64, + expect: true, + }, + { + value: []float32{11.1}, + expect: false, + }, + { + value: []float32{}, + expect: true, + }, + } + ) + + for _, tst := range tcc { + req.Equal(tst.expect, isEmpty(tst.value)) + } +} diff --git a/pkg/expr/func_num.go b/pkg/expr/func_num.go index b465a6612..1d6351e06 100644 --- a/pkg/expr/func_num.go +++ b/pkg/expr/func_num.go @@ -1,8 +1,9 @@ package expr import ( - "github.com/PaesslerAG/gval" "math" + + "github.com/PaesslerAG/gval" ) func NumericFunctions() []gval.Language { @@ -12,6 +13,12 @@ func NumericFunctions() []gval.Language { gval.Function("round", round), gval.Function("floor", floor), gval.Function("ceil", ceil), + gval.Function("abs", math.Abs), + gval.Function("log", math.Log10), + gval.Function("pow", math.Pow), + gval.Function("sqrt", math.Sqrt), + gval.Function("sum", sum), + gval.Function("average", average), } } @@ -71,3 +78,37 @@ func floor(f float64) float64 { func ceil(f float64) float64 { return math.Ceil(f) } + +func sum(v ...interface{}) float64 { + var sum float64 + + for _, vv := range v { + c, err := CastToFloat(vv) + + if err != nil { + continue + } + + sum += c + } + + return sum +} + +func average(v ...interface{}) float64 { + var i int + var j, sum float64 + + for ; i < len(v); i++ { + c, err := CastToFloat(v[i]) + + if err != nil { + continue + } + + j++ + sum += c + } + + return sum / j +} diff --git a/pkg/expr/func_num_test.go b/pkg/expr/func_num_test.go index e9a7d693e..fc499a739 100644 --- a/pkg/expr/func_num_test.go +++ b/pkg/expr/func_num_test.go @@ -3,34 +3,76 @@ package expr func Example_min() { eval(`min(2, 1, 3)`, nil) - // output - // float64(1) + // output: + // 1 } func Example_max() { eval(`max(2, 1, 3)`, nil) - // output - // float64(3) + // output: + // 3 } func Example_round() { eval(`round(3.14,1)`, nil) - // output + // output: // 3.1 } func Example_floor() { eval(`floor(3.14)`, nil) - // output - // float64(3) + // output: + // 3 } func Example_ceil() { eval(`ceil(3.14)`, nil) - // output - // float64(4) + // output: + // 4 +} + +func Example_abs() { + eval(`abs(-3.14)`, nil) + + // output: + // 3.14 +} + +func Example_log() { + eval(`log(100)`, nil) + + // output: + // 2 +} + +func Example_ln() { + eval(`pow(2, 3)`, nil) + + // output: + // 8 +} + +func Example_sqrt() { + eval(`sqrt(16)`, nil) + + // output: + // 4 +} + +func Example_sum() { + eval(`sum(1, 2, "foo", 3.4, 4, "3")`, 10.4) + + // output: + // 13.4 +} + +func Example_average() { + eval(`average(1, 2, 3, "foo", 10)`, 10.4) + + // output: + // 4 } diff --git a/pkg/expr/func_str.go b/pkg/expr/func_str.go index c89d895ba..ea3bf3a26 100644 --- a/pkg/expr/func_str.go +++ b/pkg/expr/func_str.go @@ -2,9 +2,11 @@ package expr import ( "fmt" + "regexp" "strings" "github.com/PaesslerAG/gval" + valid "github.com/asaskevich/govalidator" ) func StringFunctions() []gval.Language { @@ -18,6 +20,21 @@ func StringFunctions() []gval.Language { gval.Function("shortest", shortest), gval.Function("longest", longest), gval.Function("format", fmt.Sprintf), + gval.Function("title", title), + gval.Function("untitle", untitle), + gval.Function("repeat", strings.Repeat), + gval.Function("replace", strings.Replace), + gval.Function("isUrl", valid.IsURL), + gval.Function("isEmail", valid.IsEmail), + gval.Function("split", strings.Split), + gval.Function("join", strings.Join), + gval.Function("hasSubstring", hasSubstring), + gval.Function("substring", substring), + gval.Function("hasPrefix", strings.HasPrefix), + gval.Function("hasSuffix", strings.HasSuffix), + gval.Function("shorten", shorten), + gval.Function("camelize", camelize), + gval.Function("snakify", snakify), } } @@ -44,3 +61,87 @@ func longest(f string, aa ...string) string { func length(s string) int { return len(s) } + +// title works similarly as strings.ToTitle, with the expception +// of uppercasing only the first word in line +func title(s string) string { + split := strings.Split(s, " ") + return fmt.Sprintf("%s %s", strings.Title(split[0]), strings.Join(split[1:], " ")) +} + +// untitle works only on the first word in line +func untitle(s string) string { + split := strings.Split(s, " ") + first := strings.ToLower(split[0][:1]) + split[0][1:] + + return fmt.Sprintf("%s %s", first, strings.Join(split[1:], " ")) +} + +// hasSubstring checks if a substring exists in original string +// use watchCase if need case sensitivity +func hasSubstring(s, substring string, watchCase bool) bool { + if watchCase { + return strings.Contains(s, substring) + } + + return strings.Contains(strings.ToLower(s), strings.ToLower(substring)) +} + +// substring extracts a substring from original string +// specifying end value will not match till end of string +func substring(s string, start, end int) string { + if end == -1 { + end = len(s) + } + + if start >= len(s) { + return "" + } + + end++ + if end > len(s) { + end = len(s) + } + + return s[start:end] +} + +// shorten trims by whole words or only chars by +// the specified amount +func shorten(s, t string, count int) string { + var joined string + + if t == "char" { + if count > len(s) { + return "" + } + + joined = s[:count] + } else { + fields := strings.Fields(s) + + if len(fields) == 0 { + return "" + } + + joined = strings.Join(fields[:count], " ") + } + + reg, err := regexp.Compile("[^a-zA-Z0-9]$") + + if err != nil { + return "" + } + + joined = reg.ReplaceAllString(joined, "") + + return joined + " …" +} + +func camelize(s string) string { + return untitle(strings.Replace(strings.Title(s), " ", "", -1)) +} + +func snakify(s string) string { + return strings.ToLower(strings.Replace(strings.Title(s), " ", "_", -1)) +} diff --git a/pkg/expr/func_str_test.go b/pkg/expr/func_str_test.go index f07c4a211..85cbeadd7 100644 --- a/pkg/expr/func_str_test.go +++ b/pkg/expr/func_str_test.go @@ -42,3 +42,150 @@ func Example_longest() { // output: // foobar } + +func Example_title() { + eval(`title("foo bAR")`, nil) + + // output: + // Foo bAR +} + +func Example_untitle() { + eval(`untitle("Foo Bar")`, nil) + + // output: + // foo Bar +} + +func Example_repeat() { + eval(`repeat("duran ", c)`, map[string]interface{}{"c": 2}) + + // output: + // duran duran +} + +func Example_replace_all() { + eval(`replace("foobar baz", "ba", "BA", c)`, map[string]interface{}{"c": -1}) + + // output: + // fooBAr BAz +} + +func Example_replace_first() { + eval(`replace("foobar baz", "ba", "BA", c)`, map[string]interface{}{"c": 1}) + + // output: + // fooBAr baz +} + +func Example_isUrl() { + eval(`isUrl("http:/example.tld")`, nil) + + // output: + // false +} + +func Example_isEmail() { + eval(`isUrl("example.user+valid@example.tld")`, nil) + + // output: + // true +} + +func Example_split() { + eval(`split("This will be split in:2-parts", ":")`, nil) + + // output: + // [This will be split in 2-parts] +} + +func Example_join() { + eval(`join(exploded, ",")`, map[string][]string{"exploded": {"One", "two", "three"}}) + + // output: + // One,two,three +} + +func Example_hasSubstring_caseS() { + eval(`hasSubstring("foo BAR", "o b", true)`, nil) + + // output: + // false +} + +func Example_hasSubstring_caseI() { + eval(`hasSubstring("foo BAR", "o b", false)`, nil) + + // output: + // true +} + +func Example_hasSuffix() { + eval(`hasSuffix("foo bar", "ar")`, nil) + + // output: + // true +} + +func Example_hasPrefix() { + eval(`hasPrefix("foo bar", "foo ")`, nil) + + // output: + // true +} + +func Example_shorten_word() { + eval(`shorten("foo bar 1337, this is. test one - or three", "word", c)`, map[string]int{"c": 3}) + + // output: + // foo bar 1337 … +} + +func Example_shorten_char() { + eval(`shorten("foo bar 1337, this is. test one - or three", "char", c)`, map[string]int{"c": 22}) + + // output: + // foo bar 1337, this is … +} + +func Example_camelize() { + eval(`camelize("foo bar baz_test")`, nil) + + // output: + // fooBarBaz_test +} + +func Example_snakify() { + eval(`snakify("foo bar baz_test")`, nil) + + // output: + // foo_bar_baz_test +} + +func Example_substring() { + eval(`substring("foo bar baz_test", start, end)`, map[string]interface{}{"start": 2, "end": -1}) + + // output: + // o bar baz_test +} + +func Example_substring_highStart() { + eval(`substring("foo", start, end)`, map[string]interface{}{"start": 3, "end": -1}) + + // output: + // +} + +func Example_substring_withEnd() { + eval(`substring("foo bar baz_test", start, end)`, map[string]interface{}{"start": 2, "end": 4}) + + // output: + // o b +} + +func Example_substring_endOverflow() { + eval(`substring("foo bar baz_test", start, end)`, map[string]interface{}{"start": 2, "end": 100}) + + // output: + // o bar baz_test +} diff --git a/pkg/expr/func_time.go b/pkg/expr/func_time.go index f88d28425..8d5c6d6fb 100644 --- a/pkg/expr/func_time.go +++ b/pkg/expr/func_time.go @@ -15,9 +15,28 @@ func TimeFunctions() []gval.Language { gval.Function("modTime", modTime), gval.Function("parseDuration", time.ParseDuration), gval.Function("strftime", strfTime), + gval.Function("isLeapYear", isLeapYear), + gval.Function("now", now), + gval.Function("isWeekDay", isWeekDay), } } +func now() time.Time { + return time.Now() +} + +func isLeapYear(f time.Time) bool { + return time.Date(f.Year(), time.December, 31, 0, 0, 0, 0, time.Local).YearDay() == 366 +} + +func isLeapDay(f time.Time) bool { + return f.Day() == 29 && f.Month() == 2 +} + +func isWeekDay(f time.Time) bool { + return time.Sunday < f.Weekday() && f.Weekday() < time.Saturday +} + func earliest(f time.Time, aa ...time.Time) time.Time { for _, s := range aa { if s.Before(f) { diff --git a/pkg/expr/func_time_test.go b/pkg/expr/func_time_test.go index 7a7087b18..ad6900c9a 100644 --- a/pkg/expr/func_time_test.go +++ b/pkg/expr/func_time_test.go @@ -1,6 +1,8 @@ package expr -import "time" +import ( + "time" +) var ( @@ -17,6 +19,7 @@ var ( "hgp": hgp, "wfa": wfa, "ghd": ghd, + "now": now, } ) @@ -68,3 +71,17 @@ func Example_latest() { // output: // 1993-02-02 06:00:00 -0500 -0500 } + +func Example_isLeapYear() { + eval(`isLeapYear(ghd)`, exampleTimeParams) + + // output: + // false +} + +func Example_isWeekDay() { + eval(`isWeekDay(now)`, exampleTimeParams) + + // output: + // true +} diff --git a/pkg/expr/parser.go b/pkg/expr/parser.go index 3388a7fed..ee966d308 100644 --- a/pkg/expr/parser.go +++ b/pkg/expr/parser.go @@ -3,6 +3,7 @@ package expr import ( "context" "fmt" + "github.com/PaesslerAG/gval" ) @@ -84,6 +85,7 @@ func AllFunctions() []gval.Language { ff = append(ff, StringFunctions()...) ff = append(ff, NumericFunctions()...) ff = append(ff, TimeFunctions()...) + ff = append(ff, ArrayFunctions()...) return ff }