From 6888a9611db1a4ed75a6e52d6e17c28a4f722ef5 Mon Sep 17 00:00:00 2001 From: siyul-park Date: Tue, 10 Oct 2023 21:16:18 -0400 Subject: [PATCH] feat: change to file location --- internal/parse/error.go => error.go | 2 +- internal/parse/util.go | 99 ------------------ internal/parse/signature.go => signature.go | 110 ++++++++++---------- util.go | 98 +++++++++++++++++ internal/parse/util_test.go => util_test.go | 8 +- 5 files changed, 158 insertions(+), 159 deletions(-) rename internal/parse/error.go => error.go (94%) delete mode 100644 internal/parse/util.go rename internal/parse/signature.go => signature.go (84%) rename internal/parse/util_test.go => util_test.go (94%) diff --git a/internal/parse/error.go b/error.go similarity index 94% rename from internal/parse/error.go rename to error.go index e274715..9218f46 100644 --- a/internal/parse/error.go +++ b/error.go @@ -1,4 +1,4 @@ -package parse +package jsonata import "fmt" diff --git a/internal/parse/util.go b/internal/parse/util.go deleted file mode 100644 index daae2f6..0000000 --- a/internal/parse/util.go +++ /dev/null @@ -1,99 +0,0 @@ -package parse - -import ( - "math" - "reflect" -) - -// IsNumeric check if value is a finite number. -func IsNumeric(v any) bool { - switch v.(type) { - case int, uint, int8, int16, int32, int64, uint8, uint16, uint32, uint64: - return true - case float32, float64: - f64 := 0.0 - if f, ok := v.(float64); ok { - f64 = f - } else if f, ok := v.(float32); ok { - f64 = float64(f) - } - return !math.IsNaN(f64) && !math.IsInf(f64, 0) - default: - return false - } -} - -// IsArrayOfStrings returns true if the arg is an array of strings. -func IsArrayOfStrings(v any) bool { - if v == nil { - return true - } else if _, ok := v.([]string); ok { - return true - } else if arr, ok := v.([]any); ok { - for _, e := range arr { - if _, ok := e.(string); !ok { - return false - } - } - return true - } - return false -} - -// IsArrayOfNumbers true if the arg is an array of numbers -func IsArrayOfNumbers(v any) bool { - if v == nil { - return true - } - - switch v.(type) { - case []any, []int, []uint, []int8, []int16, []int32, []int64, []uint8, []uint16, []uint32, []uint64, []float32, []float64: - v := reflect.ValueOf(v) - for i := 0; i < v.Len(); i++ { - if !IsNumeric(v.Index(i).Interface()) { - return false - } - } - return true - } - - return false -} - -func IsNil(v any) bool { - defer func() { _ = recover() }() - return v == nil || reflect.ValueOf(v).IsNil() -} - -func ForEach(v any, f func(any, any) bool) { - rv := reflect.ValueOf(v) - - for rv.Kind() == reflect.Pointer { - rv = rv.Elem() - } - - switch rv.Kind() { - case reflect.Slice, reflect.Array: - for i := 0; i < rv.Len(); i++ { - if !f(i, rv.Index(i).Interface()) { - return - } - } - case reflect.Map: - iter := rv.MapRange() - for iter.Next() { - if !f(iter.Key().Interface(), iter.Value().Interface()) { - return - } - } - case reflect.Struct: - for i := 0; i < rv.NumField(); i++ { - if !rv.Type().Field(i).IsExported() { - continue - } - if !f(rv.Type().Field(i).Name, rv.Field(i).Interface()) { - return - } - } - } -} diff --git a/internal/parse/signature.go b/signature.go similarity index 84% rename from internal/parse/signature.go rename to signature.go index 27b3333..fdb58f9 100644 --- a/internal/parse/signature.go +++ b/signature.go @@ -1,4 +1,4 @@ -package parse +package jsonata import ( "reflect" @@ -7,12 +7,12 @@ import ( ) type ( - Validator struct { + validator struct { Definition string Validate func(args []any, ctx any) ([]any, error) } - Param struct { + param struct { Regex *regexp.Regexp Type string Subtype string @@ -33,8 +33,8 @@ var ( } ) -// ParseSignature Parses a function signature definition and returns a validation function -func ParseSignature(signature string) (*Validator, error) { +// parseSignature Parses a function signature definition and returns a validation function +func parseSignature(signature string) (*validator, error) { signatureRunes := []rune(signature) // create a Regex that represents this signature and return a function that when invoked, @@ -42,9 +42,9 @@ func ParseSignature(signature string) (*Validator, error) { // step through the signature, one symbol at a time var ( position = 1 - params = []Param{} - param = Param{} - prevParam Param = param + params = []param{} + currParam = param{} + prevParam param = currParam ) for position < len(signatureRunes) { symbol := signatureRunes[position] @@ -55,9 +55,9 @@ func ParseSignature(signature string) (*Validator, error) { } next := func() { - params = append(params, param) - prevParam = param - param = Param{} + params = append(params, currParam) + prevParam = currParam + currParam = param{} } findClosingBracket := func(str []rune, start int, openSymbol rune, closeSymbol rune) int { @@ -86,43 +86,43 @@ func ParseSignature(signature string) (*Validator, error) { if regex, err := regexp.Compile("[" + string(symbol) + "m]"); err != nil { return nil, err } else { - param.Regex = regex + currParam.Regex = regex } - param.Type = string(symbol) + currParam.Type = string(symbol) next() case 'a': // array // normally treat any value as singleton array if regex, err := regexp.Compile("[asnblfom]"); err != nil { return nil, err } else { - param.Regex = regex + currParam.Regex = regex } - param.Type = string(symbol) - param.Array = true + currParam.Type = string(symbol) + currParam.Array = true next() case 'f': // function if regex, err := regexp.Compile("f"); err != nil { return nil, err } else { - param.Regex = regex + currParam.Regex = regex } - param.Type = string(symbol) + currParam.Type = string(symbol) next() case 'j': // any JSON type if regex, err := regexp.Compile("[asnblom]"); err != nil { return nil, err } else { - param.Regex = regex + currParam.Regex = regex } - param.Type = string(symbol) + currParam.Type = string(symbol) next() case 'x': // any type if regex, err := regexp.Compile("[asnblfom]"); err != nil { return nil, err } else { - param.Regex = regex + currParam.Regex = regex } - param.Type = string(symbol) + currParam.Type = string(symbol) next() case '-': // use context if param not supplied prevParam.Context = true @@ -147,7 +147,7 @@ func ParseSignature(signature string) (*Validator, error) { if regex, err := regexp.Compile("[" + string(choice) + "m]"); err != nil { return nil, err } else { - param.Regex = regex + currParam.Regex = regex } } else { // TODO harder @@ -157,7 +157,7 @@ func ParseSignature(signature string) (*Validator, error) { Offset: position, } } - param.Type = "(" + string(choice) + ")" + currParam.Type = "(" + string(choice) + ")" position = endParen next() case '<': // type parameter - can only be applied to 'a' and 'f' @@ -188,35 +188,6 @@ func ParseSignature(signature string) (*Validator, error) { return nil, err } - getSymbol := func(value any) rune { - if IsNil(value) { - return 'l' - } - - t := reflect.TypeOf(value) - for t.Kind() == reflect.Pointer { - t = t.Elem() - } - - switch t.Kind() { - case reflect.String: - return 's' - case reflect.Int, reflect.Uint, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: - return 'n' - case reflect.Bool: - return 'b' - case reflect.Func: - return 'f' - case reflect.Array, reflect.Slice: - return 'a' - case reflect.Map, reflect.Struct: - return 'o' - default: - // any value can be undefined, but should be allowed to match - return 'm' // m for missing - } - } - throwValidationError := func(badArgs []any, badSig string) error { // to figure out where this went wrong we need apply each component of the // regex to each argument until we get to the one that fails to match @@ -248,7 +219,7 @@ func ParseSignature(signature string) (*Validator, error) { } } - return &Validator{ + return &validator{ Definition: signature, Validate: func(args []any, context any) ([]any, error) { suppliedSig := &strings.Builder{} @@ -301,7 +272,7 @@ func ParseSignature(signature string) (*Validator, error) { var itemType rune var differentItems []any - ForEach(arg, func(k, v any) bool { + forEach(arg, func(k, v any) bool { if itemType == 0 { itemType = getSymbol(v) if itemType != []rune(param.Subtype)[0] { // TODO recurse further @@ -350,3 +321,32 @@ func ParseSignature(signature string) (*Validator, error) { }, }, nil } + +func getSymbol(value any) rune { + if isNil(value) { + return 'l' + } + + t := reflect.TypeOf(value) + for t.Kind() == reflect.Pointer { + t = t.Elem() + } + + switch t.Kind() { + case reflect.String: + return 's' + case reflect.Int, reflect.Uint, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64: + return 'n' + case reflect.Bool: + return 'b' + case reflect.Func: + return 'f' + case reflect.Array, reflect.Slice: + return 'a' + case reflect.Map, reflect.Struct: + return 'o' + default: + // any value can be undefined, but should be allowed to match + return 'm' // m for missing + } +} diff --git a/util.go b/util.go index 021a745..3579762 100644 --- a/util.go +++ b/util.go @@ -1 +1,99 @@ package jsonata + +import ( + "math" + "reflect" +) + +// isNumeric check if value is a finite number. +func isNumeric(v any) bool { + switch v.(type) { + case int, uint, int8, int16, int32, int64, uint8, uint16, uint32, uint64: + return true + case float32, float64: + f64 := 0.0 + if f, ok := v.(float64); ok { + f64 = f + } else if f, ok := v.(float32); ok { + f64 = float64(f) + } + return !math.IsNaN(f64) && !math.IsInf(f64, 0) + default: + return false + } +} + +// isArrayOfStrings returns true if the arg is an array of strings. +func isArrayOfStrings(v any) bool { + if v == nil { + return true + } else if _, ok := v.([]string); ok { + return true + } else if arr, ok := v.([]any); ok { + for _, e := range arr { + if _, ok := e.(string); !ok { + return false + } + } + return true + } + return false +} + +// isArrayOfNumbers true if the arg is an array of numbers +func isArrayOfNumbers(v any) bool { + if v == nil { + return true + } + + switch v.(type) { + case []any, []int, []uint, []int8, []int16, []int32, []int64, []uint8, []uint16, []uint32, []uint64, []float32, []float64: + v := reflect.ValueOf(v) + for i := 0; i < v.Len(); i++ { + if !isNumeric(v.Index(i).Interface()) { + return false + } + } + return true + } + + return false +} + +func isNil(v any) bool { + defer func() { _ = recover() }() + return v == nil || reflect.ValueOf(v).IsNil() +} + +func forEach(v any, f func(any, any) bool) { + rv := reflect.ValueOf(v) + + for rv.Kind() == reflect.Pointer { + rv = rv.Elem() + } + + switch rv.Kind() { + case reflect.Slice, reflect.Array: + for i := 0; i < rv.Len(); i++ { + if !f(i, rv.Index(i).Interface()) { + return + } + } + case reflect.Map: + iter := rv.MapRange() + for iter.Next() { + if !f(iter.Key().Interface(), iter.Value().Interface()) { + return + } + } + case reflect.Struct: + for i := 0; i < rv.NumField(); i++ { + if !rv.Type().Field(i).IsExported() { + continue + } + if !f(rv.Type().Field(i).Name, rv.Field(i).Interface()) { + return + } + } + } +} diff --git a/internal/parse/util_test.go b/util_test.go similarity index 94% rename from internal/parse/util_test.go rename to util_test.go index e1c9cbb..8f10c37 100644 --- a/internal/parse/util_test.go +++ b/util_test.go @@ -1,4 +1,4 @@ -package parse +package jsonata import ( "fmt" @@ -53,7 +53,7 @@ func TestIsNumeric(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("%v", tc.when), func(t *testing.T) { - ok := IsNumeric(tc.when) + ok := isNumeric(tc.when) assert.Equal(t, tc.expect, ok) }) } @@ -88,7 +88,7 @@ func TestIsArrayOfStrings(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("%v", tc.when), func(t *testing.T) { - ok := IsArrayOfStrings(tc.when) + ok := isArrayOfStrings(tc.when) assert.Equal(t, tc.expect, ok) }) } @@ -131,7 +131,7 @@ func TestIsArrayOfNumbers(t *testing.T) { for _, tc := range testCases { t.Run(fmt.Sprintf("%v", tc.when), func(t *testing.T) { - ok := IsArrayOfNumbers(tc.when) + ok := isArrayOfNumbers(tc.when) assert.Equal(t, tc.expect, ok) }) }