diff --git a/config/datatype/builtin/any.go b/config/datatype/builtin/any.go new file mode 100644 index 0000000..5e3cc94 --- /dev/null +++ b/config/datatype/builtin/any.go @@ -0,0 +1,17 @@ +package builtin + +import "git.xdrm.io/go/aicra/config/datatype" + +// AnyDataType is what its name tells +type AnyDataType struct{} + +// Build returns the validator +func (AnyDataType) Build(typeName string) datatype.Validator { + // nothing if type not handled + if typeName != "any" { + return nil + } + return func(value interface{}) (interface{}, bool) { + return value, true + } +} diff --git a/typecheck/builtin/any_test.go b/config/datatype/builtin/any_test.go similarity index 67% rename from typecheck/builtin/any_test.go rename to config/datatype/builtin/any_test.go index de4f260..4c3678e 100644 --- a/typecheck/builtin/any_test.go +++ b/config/datatype/builtin/any_test.go @@ -4,26 +4,13 @@ import ( "fmt" "testing" - "git.xdrm.io/go/aicra/typecheck/builtin" + "git.xdrm.io/go/aicra/config/datatype/builtin" ) -func TestAny_New(t *testing.T) { - t.Parallel() - - inst := interface{}(builtin.NewAny()) - - switch cast := inst.(type) { - case *builtin.Any: - return - default: - t.Errorf("expect %T ; got %T", &builtin.Any{}, cast) - } -} - func TestAny_AvailableTypes(t *testing.T) { t.Parallel() - inst := builtin.NewAny() + dt := builtin.AnyDataType{} tests := []struct { Type string @@ -39,9 +26,9 @@ func TestAny_AvailableTypes(t *testing.T) { } for _, test := range tests { - checker := inst.Checker(test.Type) + validator := dt.Build(test.Type) - if checker == nil { + if validator == nil { if test.Handled { t.Errorf("expect %q to be handled", test.Type) } @@ -60,8 +47,8 @@ func TestAny_AlwaysTrue(t *testing.T) { const typeName = "any" - checker := builtin.NewAny().Checker(typeName) - if checker == nil { + validator := builtin.AnyDataType{}.Build(typeName) + if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() } @@ -76,7 +63,7 @@ func TestAny_AlwaysTrue(t *testing.T) { for i, value := range values { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - if !checker(value) { + if _, isValid := validator(value); !isValid { t.Errorf("expect value to be valid") t.Fail() } diff --git a/config/datatype/builtin/bool.go b/config/datatype/builtin/bool.go new file mode 100644 index 0000000..a57721d --- /dev/null +++ b/config/datatype/builtin/bool.go @@ -0,0 +1,31 @@ +package builtin + +import "git.xdrm.io/go/aicra/config/datatype" + +// BoolDataType is what its name tells +type BoolDataType struct{} + +// Build returns the validator +func (BoolDataType) Build(typeName string) datatype.Validator { + // nothing if type not handled + if typeName != "bool" { + return nil + } + + return func(value interface{}) (interface{}, bool) { + switch cast := value.(type) { + case bool: + return cast, true + + case string: + strVal := string(cast) + return strVal == "true", strVal == "true" || strVal == "false" + case []byte: + strVal := string(cast) + return strVal == "true", strVal == "true" || strVal == "false" + + default: + return false, false + } + } +} diff --git a/typecheck/builtin/bool_test.go b/config/datatype/builtin/bool_test.go similarity index 75% rename from typecheck/builtin/bool_test.go rename to config/datatype/builtin/bool_test.go index 8e63344..52ef4c2 100644 --- a/typecheck/builtin/bool_test.go +++ b/config/datatype/builtin/bool_test.go @@ -4,26 +4,13 @@ import ( "fmt" "testing" - "git.xdrm.io/go/aicra/typecheck/builtin" + "git.xdrm.io/go/aicra/config/datatype/builtin" ) -func TestBool_New(t *testing.T) { - t.Parallel() - - inst := interface{}(builtin.NewBool()) - - switch cast := inst.(type) { - case *builtin.Bool: - return - default: - t.Errorf("expect %T ; got %T", &builtin.Bool{}, cast) - } -} - func TestBool_AvailableTypes(t *testing.T) { t.Parallel() - inst := builtin.NewBool() + dt := builtin.BoolDataType{} tests := []struct { Type string @@ -39,8 +26,8 @@ func TestBool_AvailableTypes(t *testing.T) { for _, test := range tests { t.Run(test.Type, func(t *testing.T) { - checker := inst.Checker(test.Type) - if checker == nil { + validator := dt.Build(test.Type) + if validator == nil { if test.Handled { t.Errorf("expect %q to be handled", test.Type) t.Fail() @@ -62,8 +49,8 @@ func TestBool_Values(t *testing.T) { const typeName = "bool" - checker := builtin.NewBool().Checker(typeName) - if checker == nil { + validator := builtin.BoolDataType{}.Build(typeName) + if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() } @@ -98,7 +85,7 @@ func TestBool_Values(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - if checker(test.Value) { + if _, isValid := validator(test.Value); isValid { if !test.Valid { t.Errorf("expect value to be invalid") t.Fail() diff --git a/config/datatype/builtin/float.go b/config/datatype/builtin/float.go new file mode 100644 index 0000000..1e2fd18 --- /dev/null +++ b/config/datatype/builtin/float.go @@ -0,0 +1,47 @@ +package builtin + +import ( + "encoding/json" + + "git.xdrm.io/go/aicra/config/datatype" +) + +// FloatDataType is what its name tells +type FloatDataType struct{} + +// Build returns the validator +func (FloatDataType) Build(typeName string) datatype.Validator { + // nothing if type not handled + if typeName != "float64" && typeName != "float" { + return nil + } + return func(value interface{}) (interface{}, bool) { + switch cast := value.(type) { + + case int: + return float64(cast), true + + case uint: + return float64(cast), true + + case float64: + return cast, true + + // serialized string -> try to convert to float + case []byte: + num := json.Number(cast) + floatVal, err := num.Float64() + return floatVal, err == nil + + case string: + num := json.Number(cast) + floatVal, err := num.Float64() + return floatVal, err == nil + + // unknown type + default: + return 0, false + + } + } +} diff --git a/typecheck/builtin/float64_test.go b/config/datatype/builtin/float_test.go similarity index 80% rename from typecheck/builtin/float64_test.go rename to config/datatype/builtin/float_test.go index f1c10fc..f862521 100644 --- a/typecheck/builtin/float64_test.go +++ b/config/datatype/builtin/float_test.go @@ -5,26 +5,13 @@ import ( "math" "testing" - "git.xdrm.io/go/aicra/typecheck/builtin" + "git.xdrm.io/go/aicra/config/datatype/builtin" ) -func TestFloat64_New(t *testing.T) { - t.Parallel() - - inst := interface{}(builtin.NewFloat64()) - - switch cast := inst.(type) { - case *builtin.Float64: - return - default: - t.Errorf("expect %T ; got %T", &builtin.Float64{}, cast) - } -} - func TestFloat64_AvailableTypes(t *testing.T) { t.Parallel() - inst := builtin.NewFloat64() + dt := builtin.FloatDataType{} tests := []struct { Type string @@ -46,8 +33,8 @@ func TestFloat64_AvailableTypes(t *testing.T) { for _, test := range tests { t.Run(test.Type, func(t *testing.T) { - checker := inst.Checker(test.Type) - if checker == nil { + validator := dt.Build(test.Type) + if validator == nil { if test.Handled { t.Errorf("expect %q to be handled", test.Type) t.Fail() @@ -69,8 +56,8 @@ func TestFloat64_Values(t *testing.T) { const typeName = "float" - checker := builtin.NewFloat64().Checker(typeName) - if checker == nil { + validator := builtin.FloatDataType{}.Build(typeName) + if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() } @@ -110,7 +97,7 @@ func TestFloat64_Values(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - if checker(test.Value) { + if _, isValid := validator(test.Value); isValid { if !test.Valid { t.Errorf("expect value to be invalid") t.Fail() diff --git a/config/datatype/builtin/int.go b/config/datatype/builtin/int.go new file mode 100644 index 0000000..a82e2ad --- /dev/null +++ b/config/datatype/builtin/int.go @@ -0,0 +1,52 @@ +package builtin + +import ( + "encoding/json" + "math" + + "git.xdrm.io/go/aicra/config/datatype" +) + +// IntDataType is what its name tells +type IntDataType struct{} + +// Build returns the validator +func (IntDataType) Build(typeName string) datatype.Validator { + // nothing if type not handled + if typeName != "int" { + return nil + } + + return func(value interface{}) (interface{}, bool) { + switch cast := value.(type) { + + case int: + return cast, true + + case uint: + overflows := cast > math.MaxInt64 + return int(cast), !overflows + + case float64: + intVal := int(cast) + overflows := cast < float64(math.MinInt64) || cast > float64(math.MaxInt64) + return intVal, cast == float64(intVal) && !overflows + + // serialized string -> try to convert to float + case string: + num := json.Number(cast) + intVal, err := num.Int64() + return int(intVal), err == nil + // serialized string -> try to convert to float + + case []byte: + num := json.Number(cast) + intVal, err := num.Int64() + return int(intVal), err == nil + + // unknown type + default: + return 0, false + } + } +} diff --git a/typecheck/builtin/int_test.go b/config/datatype/builtin/int_test.go similarity index 81% rename from typecheck/builtin/int_test.go rename to config/datatype/builtin/int_test.go index 0461847..c3126f3 100644 --- a/typecheck/builtin/int_test.go +++ b/config/datatype/builtin/int_test.go @@ -5,26 +5,13 @@ import ( "math" "testing" - "git.xdrm.io/go/aicra/typecheck/builtin" + "git.xdrm.io/go/aicra/config/datatype/builtin" ) -func TestInt_New(t *testing.T) { - t.Parallel() - - inst := interface{}(builtin.NewInt()) - - switch cast := inst.(type) { - case *builtin.Int: - return - default: - t.Errorf("expect %T ; got %T", &builtin.Int{}, cast) - } -} - func TestInt_AvailableTypes(t *testing.T) { t.Parallel() - inst := builtin.NewInt() + dt := builtin.IntDataType{} tests := []struct { Type string @@ -40,8 +27,8 @@ func TestInt_AvailableTypes(t *testing.T) { for _, test := range tests { t.Run(test.Type, func(t *testing.T) { - checker := inst.Checker(test.Type) - if checker == nil { + validator := dt.Build(test.Type) + if validator == nil { if test.Handled { t.Errorf("expect %q to be handled", test.Type) t.Fail() @@ -63,8 +50,8 @@ func TestInt_Values(t *testing.T) { const typeName = "int" - checker := builtin.NewInt().Checker(typeName) - if checker == nil { + validator := builtin.IntDataType{}.Build(typeName) + if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() } @@ -110,7 +97,7 @@ func TestInt_Values(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - if checker(test.Value) { + if _, isValid := validator(test.Value); isValid { if !test.Valid { t.Errorf("expect value to be invalid") t.Fail() diff --git a/typecheck/builtin/string.go b/config/datatype/builtin/string.go similarity index 72% rename from typecheck/builtin/string.go rename to config/datatype/builtin/string.go index 02501a1..95d1fff 100644 --- a/typecheck/builtin/string.go +++ b/config/datatype/builtin/string.go @@ -4,28 +4,24 @@ import ( "regexp" "strconv" - "git.xdrm.io/go/aicra/typecheck" + "git.xdrm.io/go/aicra/config/datatype" ) var fixedLengthRegex = regexp.MustCompile(`^string\((\d+)\)$`) var variableLengthRegex = regexp.MustCompile(`^string\((\d+), ?(\d+)\)$`) -// String checks if a value is a string -type String struct{} +// StringDataType is what its name tells +type StringDataType struct{} -// NewString returns a bare string type checker -func NewString() *String { - return &String{} -} - -// Checker returns the checker function. Availables type names are : `string`, `string(length)` and `string(minLength, maxLength)`. -func (s String) Checker(typeName string) typecheck.CheckerFunc { - isSimpleString := typeName == "string" +// Build returns the validator. +// availables type names are : `string`, `string(length)` and `string(minLength, maxLength)`. +func (s StringDataType) Build(typeName string) datatype.Validator { + simple := typeName == "string" fixedLengthMatches := fixedLengthRegex.FindStringSubmatch(typeName) variableLengthMatches := variableLengthRegex.FindStringSubmatch(typeName) // nothing if type not handled - if !isSimpleString && fixedLengthMatches == nil && variableLengthMatches == nil { + if !simple && fixedLengthMatches == nil && variableLengthMatches == nil { return nil } @@ -53,10 +49,10 @@ func (s String) Checker(typeName string) typecheck.CheckerFunc { max = exMax } - return func(value interface{}) bool { + return func(value interface{}) (interface{}, bool) { // preprocessing error if mustFail { - return false + return "", false } // check type @@ -68,21 +64,21 @@ func (s String) Checker(typeName string) typecheck.CheckerFunc { } if !isString { - return false + return "", false } - if isSimpleString { - return true + if simple { + return strValue, true } // check length against previously extracted length l := len(strValue) - return l >= min && l <= max + return strValue, l >= min && l <= max } } // getFixedLength returns the fixed length from regex matches and a success state. -func (String) getFixedLength(regexMatches []string) (int, bool) { +func (StringDataType) getFixedLength(regexMatches []string) (int, bool) { // incoherence error if regexMatches == nil || len(regexMatches) < 2 { return 0, false @@ -98,7 +94,7 @@ func (String) getFixedLength(regexMatches []string) (int, bool) { } // getVariableLength returns the length min and max from regex matches and a success state. -func (String) getVariableLength(regexMatches []string) (int, int, bool) { +func (StringDataType) getVariableLength(regexMatches []string) (int, int, bool) { // incoherence error if regexMatches == nil || len(regexMatches) < 3 { return 0, 0, false diff --git a/typecheck/builtin/string_test.go b/config/datatype/builtin/string_test.go similarity index 85% rename from typecheck/builtin/string_test.go rename to config/datatype/builtin/string_test.go index d23b7bf..e332ea3 100644 --- a/typecheck/builtin/string_test.go +++ b/config/datatype/builtin/string_test.go @@ -4,26 +4,13 @@ import ( "fmt" "testing" - "git.xdrm.io/go/aicra/typecheck/builtin" + "git.xdrm.io/go/aicra/config/datatype/builtin" ) -func TestString_New(t *testing.T) { - t.Parallel() - - inst := interface{}(builtin.NewString()) - - switch cast := inst.(type) { - case *builtin.String: - return - default: - t.Errorf("expect %T ; got %T", &builtin.String{}, cast) - } -} - func TestString_AvailableTypes(t *testing.T) { t.Parallel() - inst := builtin.NewString() + dt := builtin.StringDataType{} tests := []struct { Type string @@ -66,9 +53,9 @@ func TestString_AvailableTypes(t *testing.T) { for _, test := range tests { t.Run(test.Type, func(t *testing.T) { - checker := inst.Checker(test.Type) + validator := dt.Build(test.Type) - if checker == nil { + if validator == nil { if test.Handled { t.Errorf("expect %q to be handled", test.Type) } @@ -88,8 +75,8 @@ func TestString_AnyLength(t *testing.T) { const typeName = "string" - checker := builtin.NewString().Checker(typeName) - if checker == nil { + validator := builtin.StringDataType{}.Build(typeName) + if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() } @@ -107,7 +94,7 @@ func TestString_AnyLength(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - if checker(test.Value) { + if _, isValid := validator(test.Value); isValid { if !test.Valid { t.Errorf("expect value to be invalid") t.Fail() @@ -146,14 +133,14 @@ func TestString_FixedLength(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - checker := builtin.NewString().Checker(test.Type) - if checker == nil { + validator := builtin.StringDataType{}.Build(test.Type) + if validator == nil { t.Errorf("expect %q to be handled", test.Type) t.Fail() return } - if checker(test.Value) { + if _, isValid := validator(test.Value); isValid { if !test.Valid { t.Errorf("expect value to be invalid") t.Fail() @@ -207,14 +194,14 @@ func TestString_VariableLength(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - checker := builtin.NewString().Checker(test.Type) - if checker == nil { + validator := builtin.StringDataType{}.Build(test.Type) + if validator == nil { t.Errorf("expect %q to be handled", test.Type) t.Fail() return } - if checker(test.Value) { + if _, isValid := validator(test.Value); isValid { if !test.Valid { t.Errorf("expect value to be invalid") t.Fail() diff --git a/config/datatype/builtin/uint.go b/config/datatype/builtin/uint.go new file mode 100644 index 0000000..ab02728 --- /dev/null +++ b/config/datatype/builtin/uint.go @@ -0,0 +1,58 @@ +package builtin + +import ( + "encoding/json" + "math" + + "git.xdrm.io/go/aicra/config/datatype" +) + +// UintDataType is what its name tells +type UintDataType struct{} + +// Build returns the validator +func (UintDataType) Build(typeName string) datatype.Validator { + // nothing if type not handled + if typeName != "uint" { + return nil + } + + return func(value interface{}) (interface{}, bool) { + switch cast := value.(type) { + + case int: + return uint(cast), cast >= 0 + + case uint: + return cast, true + + case float64: + uintVal := uint(cast) + overflows := cast < 0 || cast > math.MaxUint64 + return uintVal, cast == float64(uintVal) && !overflows + + // serialized string -> try to convert to float + case string: + num := json.Number(cast) + floatVal, err := num.Float64() + if err != nil { + return 0, false + } + overflows := floatVal < 0 || floatVal > math.MaxUint64 + return uint(floatVal), !overflows + + case []byte: + num := json.Number(cast) + floatVal, err := num.Float64() + if err != nil { + return 0, false + } + overflows := floatVal < 0 || floatVal > math.MaxUint64 + return uint(floatVal), !overflows + + // unknown type + default: + return 0, false + } + } +} diff --git a/typecheck/builtin/uint_test.go b/config/datatype/builtin/uint_test.go similarity index 82% rename from typecheck/builtin/uint_test.go rename to config/datatype/builtin/uint_test.go index 4a6ceb8..ae4ef03 100644 --- a/typecheck/builtin/uint_test.go +++ b/config/datatype/builtin/uint_test.go @@ -5,26 +5,13 @@ import ( "math" "testing" - "git.xdrm.io/go/aicra/typecheck/builtin" + "git.xdrm.io/go/aicra/config/datatype/builtin" ) -func TestUint_New(t *testing.T) { - t.Parallel() - - inst := interface{}(builtin.NewUint()) - - switch cast := inst.(type) { - case *builtin.Uint: - return - default: - t.Errorf("expect %T ; got %T", &builtin.Uint{}, cast) - } -} - func TestUint_AvailableTypes(t *testing.T) { t.Parallel() - inst := builtin.NewUint() + dt := builtin.UintDataType{} tests := []struct { Type string @@ -40,8 +27,8 @@ func TestUint_AvailableTypes(t *testing.T) { for _, test := range tests { t.Run(test.Type, func(t *testing.T) { - checker := inst.Checker(test.Type) - if checker == nil { + validator := dt.Build(test.Type) + if validator == nil { if test.Handled { t.Errorf("expect %q to be handled", test.Type) t.Fail() @@ -63,8 +50,8 @@ func TestUint_Values(t *testing.T) { const typeName = "uint" - checker := builtin.NewUint().Checker(typeName) - if checker == nil { + validator := builtin.UintDataType{}.Build(typeName) + if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() } @@ -110,7 +97,7 @@ func TestUint_Values(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - if checker(test.Value) { + if _, isValid := validator(test.Value); isValid { if !test.Valid { t.Errorf("expect value to be invalid") t.Fail() diff --git a/typecheck/builtin/any.go b/typecheck/builtin/any.go deleted file mode 100644 index 9be7254..0000000 --- a/typecheck/builtin/any.go +++ /dev/null @@ -1,22 +0,0 @@ -package builtin - -import "git.xdrm.io/go/aicra/typecheck" - -// Any is a permissive type checker -type Any struct{} - -// NewAny returns a bare any type checker -func NewAny() *Any { - return &Any{} -} - -// Checker returns the checker function -func (Any) Checker(typeName string) typecheck.CheckerFunc { - // nothing if type not handled - if typeName != "any" { - return nil - } - return func(interface{}) bool { - return true - } -} diff --git a/typecheck/builtin/bool.go b/typecheck/builtin/bool.go deleted file mode 100644 index 2d5ae07..0000000 --- a/typecheck/builtin/bool.go +++ /dev/null @@ -1,44 +0,0 @@ -package builtin - -import "git.xdrm.io/go/aicra/typecheck" - -// Bool checks if a value is a boolean -type Bool struct{} - -// NewBool returns a bare boolean type checker -func NewBool() *Bool { - return &Bool{} -} - -// Checker returns the checker function -func (Bool) Checker(typeName string) typecheck.CheckerFunc { - // nothing if type not handled - if typeName != "bool" { - return nil - } - return func(value interface{}) bool { - _, isBool := readBool(value) - return isBool - } -} - -// readBool tries to read a serialized boolean and returns whether it succeeded. -func readBool(value interface{}) (bool, bool) { - switch cast := value.(type) { - case bool: - return cast, true - - case string: - strVal := string(cast) - return strVal == "true", strVal == "true" || strVal == "false" - - case []byte: - strVal := string(cast) - return strVal == "true", strVal == "true" || strVal == "false" - - default: - return false, false - } - - return false, false -} diff --git a/typecheck/builtin/float64.go b/typecheck/builtin/float64.go deleted file mode 100644 index 081ac06..0000000 --- a/typecheck/builtin/float64.go +++ /dev/null @@ -1,58 +0,0 @@ -package builtin - -import ( - "encoding/json" - - "git.xdrm.io/go/aicra/typecheck" -) - -// Float64 checks if a value is a float64 -type Float64 struct{} - -// NewFloat64 returns a bare number type checker -func NewFloat64() *Float64 { - return &Float64{} -} - -// Checker returns the checker function -func (Float64) Checker(typeName string) typecheck.CheckerFunc { - // nothing if type not handled - if typeName != "float64" && typeName != "float" { - return nil - } - return func(value interface{}) bool { - _, isFloat := readFloat(value) - return isFloat - } -} - -// readFloat tries to read a serialized float and returns whether it succeeded. -func readFloat(value interface{}) (float64, bool) { - switch cast := value.(type) { - - case int: - return float64(cast), true - - case uint: - return float64(cast), true - - case float64: - return cast, true - - // serialized string -> try to convert to float - case string: - num := json.Number(cast) - floatVal, err := num.Float64() - return floatVal, err == nil - - case []byte: - num := json.Number(cast) - floatVal, err := num.Float64() - return floatVal, err == nil - - // unknown type - default: - return 0, false - - } -} diff --git a/typecheck/builtin/int.go b/typecheck/builtin/int.go deleted file mode 100644 index a64c0df..0000000 --- a/typecheck/builtin/int.go +++ /dev/null @@ -1,64 +0,0 @@ -package builtin - -import ( - "encoding/json" - "math" - - "git.xdrm.io/go/aicra/typecheck" -) - -// Int checks if a value is an int -type Int struct{} - -// NewInt returns a bare number type checker -func NewInt() *Int { - return &Int{} -} - -// Checker returns the checker function -func (Int) Checker(typeName string) typecheck.CheckerFunc { - // nothing if type not handled - if typeName != "int" { - return nil - } - return func(value interface{}) bool { - _, isInt := readInt(value) - - return isInt - } -} - -// readInt tries to read a serialized int and returns whether it succeeded. -func readInt(value interface{}) (int, bool) { - switch cast := value.(type) { - - case int: - return cast, true - - case uint: - overflows := cast > math.MaxInt64 - return int(cast), !overflows - - case float64: - intVal := int(cast) - overflows := cast < float64(math.MinInt64) || cast > float64(math.MaxInt64) - return intVal, cast == float64(intVal) && !overflows - - // serialized string -> try to convert to float - case string: - num := json.Number(cast) - intVal, err := num.Int64() - return int(intVal), err == nil - // serialized string -> try to convert to float - - case []byte: - num := json.Number(cast) - intVal, err := num.Int64() - return int(intVal), err == nil - - // unknown type - default: - return 0, false - - } -} diff --git a/typecheck/builtin/uint.go b/typecheck/builtin/uint.go deleted file mode 100644 index 7feb9a4..0000000 --- a/typecheck/builtin/uint.go +++ /dev/null @@ -1,70 +0,0 @@ -package builtin - -import ( - "encoding/json" - "math" - - "git.xdrm.io/go/aicra/typecheck" -) - -// Uint checks if a value is an uint -type Uint struct{} - -// NewUint returns a bare number type checker -func NewUint() *Uint { - return &Uint{} -} - -// Checker returns the checker function -func (Uint) Checker(typeName string) typecheck.CheckerFunc { - // nothing if type not handled - if typeName != "uint" { - return nil - } - return func(value interface{}) bool { - _, isInt := readUint(value) - - return isInt - } -} - -// readUint tries to read a serialized uint and returns whether it succeeded. -func readUint(value interface{}) (uint, bool) { - switch cast := value.(type) { - - case int: - return uint(cast), cast >= 0 - - case uint: - return cast, true - - case float64: - uintVal := uint(cast) - overflows := cast < 0 || cast > math.MaxUint64 - return uintVal, cast == float64(uintVal) && !overflows - - // serialized string -> try to convert to float - case string: - num := json.Number(cast) - floatVal, err := num.Float64() - if err != nil { - return 0, false - } - overflows := floatVal < 0 || floatVal > math.MaxUint64 - return uint(floatVal), !overflows - - case []byte: - num := json.Number(cast) - floatVal, err := num.Float64() - if err != nil { - return 0, false - } - overflows := floatVal < 0 || floatVal > math.MaxUint64 - return uint(floatVal), !overflows - - // unknown type - default: - return 0, false - - } -}