Added time, array, num and generic gval functions
This commit is contained in:
@@ -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()
|
||||
}
|
||||
@@ -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:
|
||||
// <nil>
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
+62
-1
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
+42
-1
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user