From defa2c3645fcd254092081e028a3a99d0c933fff Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Mon, 21 Jun 2021 21:08:22 +0200 Subject: [PATCH 1/2] refactor: rename semantics of datatype to validator.Type --- README.md | 10 ++-- builder.go | 12 ++-- builder_test.go | 18 +++--- datatype/datatype.go | 23 -------- handler_test.go | 14 ++--- internal/config/config.go | 10 ++-- internal/config/config_test.go | 26 ++++----- internal/config/parameter.go | 14 ++--- internal/config/service.go | 8 +-- internal/dynfunc/signature.go | 6 +- {datatype => validator}/builtin/any.go | 10 ++-- {datatype => validator}/builtin/any_test.go | 6 +- {datatype => validator}/builtin/bool.go | 10 ++-- {datatype => validator}/builtin/bool_test.go | 6 +- {datatype => validator}/builtin/float.go | 10 ++-- {datatype => validator}/builtin/float_test.go | 6 +- {datatype => validator}/builtin/int.go | 10 ++-- {datatype => validator}/builtin/int_test.go | 6 +- {datatype => validator}/builtin/string.go | 10 ++-- .../builtin/string_test.go | 10 ++-- {datatype => validator}/builtin/uint.go | 10 ++-- {datatype => validator}/builtin/uint_test.go | 6 +- validator/validator.go | 55 +++++++++++++++++++ 23 files changed, 164 insertions(+), 132 deletions(-) delete mode 100644 datatype/datatype.go rename {datatype => validator}/builtin/any.go (54%) rename {datatype => validator}/builtin/any_test.go (87%) rename {datatype => validator}/builtin/bool.go (70%) rename {datatype => validator}/builtin/bool_test.go (91%) rename {datatype => validator}/builtin/float.go (75%) rename {datatype => validator}/builtin/float_test.go (93%) rename {datatype => validator}/builtin/int.go (79%) rename {datatype => validator}/builtin/int_test.go (93%) rename {datatype => validator}/builtin/string.go (91%) rename {datatype => validator}/builtin/string_test.go (94%) rename {datatype => validator}/builtin/uint.go (81%) rename {datatype => validator}/builtin/uint_test.go (93%) create mode 100644 validator/validator.go diff --git a/README.md b/README.md index 9c03075..467fbc1 100644 --- a/README.md +++ b/README.md @@ -89,16 +89,16 @@ import ( "github.com/xdrm-io/aicra" "github.com/xdrm-io/aicra/api" - "github.com/xdrm-io/aicra/datatype/builtin" + "github.com/xdrm-io/aicra/validator/builtin" ) func main() { builder := &aicra.Builder{} - // register data validators - builder.AddType(builtin.BoolDataType{}) - builder.AddType(builtin.UintDataType{}) - builder.AddType(builtin.StringDataType{}) + // add custom type validators + builder.Validate(builtin.BoolDataType{}) + builder.Validate(builtin.UintDataType{}) + builder.Validate(builtin.StringDataType{}) // load your configuration config, err := os.Open("api.json") diff --git a/builder.go b/builder.go index 0fc8d3b..fc17243 100644 --- a/builder.go +++ b/builder.go @@ -5,9 +5,9 @@ import ( "io" "net/http" - "github.com/xdrm-io/aicra/datatype" "github.com/xdrm-io/aicra/internal/config" "github.com/xdrm-io/aicra/internal/dynfunc" + "github.com/xdrm-io/aicra/validator" ) // Builder for an aicra server @@ -31,18 +31,18 @@ type apiHandler struct { dyn *dynfunc.Handler } -// AddType adds an available datatype to the api definition -func (b *Builder) AddType(t datatype.T) error { +// Validate adds an available Type to validate in the api definition +func (b *Builder) Validate(t validator.Type) error { if b.conf == nil { b.conf = &config.Server{} } if b.conf.Services != nil { return errLateType } - if b.conf.Types == nil { - b.conf.Types = make([]datatype.T, 0) + if b.conf.Validators == nil { + b.conf.Validators = make([]validator.Type, 0) } - b.conf.Types = append(b.conf.Types, t) + b.conf.Validators = append(b.conf.Validators, t) return nil } diff --git a/builder_test.go b/builder_test.go index 8bae2ac..457449b 100644 --- a/builder_test.go +++ b/builder_test.go @@ -8,26 +8,26 @@ import ( "testing" "github.com/xdrm-io/aicra/api" - "github.com/xdrm-io/aicra/datatype/builtin" + "github.com/xdrm-io/aicra/validator/builtin" ) func addBuiltinTypes(b *Builder) error { - if err := b.AddType(builtin.AnyDataType{}); err != nil { + if err := b.Validate(builtin.AnyDataType{}); err != nil { return err } - if err := b.AddType(builtin.BoolDataType{}); err != nil { + if err := b.Validate(builtin.BoolDataType{}); err != nil { return err } - if err := b.AddType(builtin.FloatDataType{}); err != nil { + if err := b.Validate(builtin.FloatDataType{}); err != nil { return err } - if err := b.AddType(builtin.IntDataType{}); err != nil { + if err := b.Validate(builtin.IntDataType{}); err != nil { return err } - if err := b.AddType(builtin.StringDataType{}); err != nil { + if err := b.Validate(builtin.StringDataType{}); err != nil { return err } - if err := b.AddType(builtin.UintDataType{}); err != nil { + if err := b.Validate(builtin.UintDataType{}); err != nil { return err } return nil @@ -35,7 +35,7 @@ func addBuiltinTypes(b *Builder) error { func TestAddType(t *testing.T) { builder := &Builder{} - err := builder.AddType(builtin.BoolDataType{}) + err := builder.Validate(builtin.BoolDataType{}) if err != nil { t.Fatalf("unexpected error: %s", err) } @@ -43,7 +43,7 @@ func TestAddType(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %s", err) } - err = builder.AddType(builtin.FloatDataType{}) + err = builder.Validate(builtin.FloatDataType{}) if err != errLateType { t.Fatalf("expected <%v> got <%v>", errLateType, err) } diff --git a/datatype/datatype.go b/datatype/datatype.go deleted file mode 100644 index 2e0a264..0000000 --- a/datatype/datatype.go +++ /dev/null @@ -1,23 +0,0 @@ -package datatype - -import ( - "reflect" -) - -// Validator returns whether a given value fulfills the datatype -// and casts the value into a common go type. -// -// for example, if a validator checks for upper case strings, -// whether the value is a []byte, a string or a []rune, if the -// value matches the validator's checks, it will be cast it into -// a common go type, say, string. -type Validator func(value interface{}) (cast interface{}, valid bool) - -// T represents a datatype. The Build function returns a Validator if -// it manages types with the name `typeDefinition` (from the configuration field "type"); else it or returns NIL if the type -// definition does not match this datatype; the registry is passed to allow recursive datatypes (e.g. slices, structs, etc) -// The datatype's validator (when input is valid) must return a cast's go type matching the `Type() reflect.Type` -type T interface { - Type() reflect.Type - Build(typeDefinition string, registry ...T) Validator -} diff --git a/handler_test.go b/handler_test.go index 00311f1..51e9719 100644 --- a/handler_test.go +++ b/handler_test.go @@ -12,26 +12,26 @@ import ( "github.com/xdrm-io/aicra" "github.com/xdrm-io/aicra/api" - "github.com/xdrm-io/aicra/datatype/builtin" + "github.com/xdrm-io/aicra/validator/builtin" ) func addBuiltinTypes(b *aicra.Builder) error { - if err := b.AddType(builtin.AnyDataType{}); err != nil { + if err := b.Validate(builtin.AnyDataType{}); err != nil { return err } - if err := b.AddType(builtin.BoolDataType{}); err != nil { + if err := b.Validate(builtin.BoolDataType{}); err != nil { return err } - if err := b.AddType(builtin.FloatDataType{}); err != nil { + if err := b.Validate(builtin.FloatDataType{}); err != nil { return err } - if err := b.AddType(builtin.IntDataType{}); err != nil { + if err := b.Validate(builtin.IntDataType{}); err != nil { return err } - if err := b.AddType(builtin.StringDataType{}); err != nil { + if err := b.Validate(builtin.StringDataType{}); err != nil { return err } - if err := b.AddType(builtin.UintDataType{}); err != nil { + if err := b.Validate(builtin.UintDataType{}); err != nil { return err } return nil diff --git a/internal/config/config.go b/internal/config/config.go index 1ac6fcc..cc529d0 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -7,13 +7,13 @@ import ( "net/http" "strings" - "github.com/xdrm-io/aicra/datatype" + "github.com/xdrm-io/aicra/validator" ) // Server definition type Server struct { - Types []datatype.T - Services []*Service + Validators []validator.Type + Services []*Service } // Parse a configuration into a server. Server.Types must be set beforehand to @@ -32,9 +32,9 @@ func (srv *Server) Parse(r io.Reader) error { } // validate implements the validator interface -func (server Server) validate(datatypes ...datatype.T) error { +func (server Server) validate(datatypes ...validator.Type) error { for _, service := range server.Services { - err := service.validate(server.Types...) + err := service.validate(server.Validators...) if err != nil { return fmt.Errorf("%s '%s': %w", service.Method, service.Pattern, err) } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 030e027..2bf019f 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - "github.com/xdrm-io/aicra/datatype/builtin" + "github.com/xdrm-io/aicra/validator/builtin" ) func TestLegalServiceName(t *testing.T) { @@ -239,7 +239,7 @@ func TestParamEmptyRenameNoRename(t *testing.T) { } ]`) srv := &Server{} - srv.Types = append(srv.Types, builtin.AnyDataType{}) + srv.Validators = append(srv.Validators, builtin.AnyDataType{}) err := srv.Parse(r) if err != nil { t.Errorf("unexpected error: '%s'", err) @@ -275,8 +275,8 @@ func TestOptionalParam(t *testing.T) { } ]`) srv := &Server{} - srv.Types = append(srv.Types, builtin.AnyDataType{}) - srv.Types = append(srv.Types, builtin.BoolDataType{}) + srv.Validators = append(srv.Validators, builtin.AnyDataType{}) + srv.Validators = append(srv.Validators, builtin.BoolDataType{}) err := srv.Parse(r) if err != nil { t.Errorf("unexpected error: '%s'", err) @@ -588,7 +588,7 @@ func TestParseParameters(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { srv := &Server{} - srv.Types = append(srv.Types, builtin.AnyDataType{}) + srv.Validators = append(srv.Validators, builtin.AnyDataType{}) err := srv.Parse(strings.NewReader(test.Raw)) if err == nil && test.Error != nil { @@ -827,8 +827,8 @@ func TestServiceCollision(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { srv := &Server{} - srv.Types = append(srv.Types, builtin.StringDataType{}) - srv.Types = append(srv.Types, builtin.UintDataType{}) + srv.Validators = append(srv.Validators, builtin.StringDataType{}) + srv.Validators = append(srv.Validators, builtin.UintDataType{}) err := srv.Parse(strings.NewReader(test.Config)) if err == nil && test.Error != nil { @@ -997,9 +997,9 @@ func TestMatchSimple(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { srv := &Server{} - srv.Types = append(srv.Types, builtin.AnyDataType{}) - srv.Types = append(srv.Types, builtin.IntDataType{}) - srv.Types = append(srv.Types, builtin.BoolDataType{}) + srv.Validators = append(srv.Validators, builtin.AnyDataType{}) + srv.Validators = append(srv.Validators, builtin.IntDataType{}) + srv.Validators = append(srv.Validators, builtin.BoolDataType{}) err := srv.Parse(strings.NewReader(test.Config)) if err != nil { @@ -1081,9 +1081,9 @@ func TestFindPriority(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { srv := &Server{} - srv.Types = append(srv.Types, builtin.AnyDataType{}) - srv.Types = append(srv.Types, builtin.IntDataType{}) - srv.Types = append(srv.Types, builtin.BoolDataType{}) + srv.Validators = append(srv.Validators, builtin.AnyDataType{}) + srv.Validators = append(srv.Validators, builtin.IntDataType{}) + srv.Validators = append(srv.Validators, builtin.BoolDataType{}) err := srv.Parse(strings.NewReader(test.Config)) if err != nil { diff --git a/internal/config/parameter.go b/internal/config/parameter.go index d0e5ab7..a22227b 100644 --- a/internal/config/parameter.go +++ b/internal/config/parameter.go @@ -3,7 +3,7 @@ package config import ( "reflect" - "github.com/xdrm-io/aicra/datatype" + "github.com/xdrm-io/aicra/validator" ) // Parameter represents a parameter definition (from api.json) @@ -12,13 +12,13 @@ type Parameter struct { Type string `json:"type"` Rename string `json:"name,omitempty"` Optional bool - // ExtractType is the type the Validator will cast into - ExtractType reflect.Type + // GoType is the type the Validator will cast into + GoType reflect.Type // Validator is inferred from the "type" property - Validator datatype.Validator + Validator validator.ValidateFunc } -func (param *Parameter) validate(datatypes ...datatype.T) error { +func (param *Parameter) validate(datatypes ...validator.Type) error { if len(param.Description) < 1 { return errMissingParamDesc } @@ -35,8 +35,8 @@ func (param *Parameter) validate(datatypes ...datatype.T) error { // find validator for _, dtype := range datatypes { - param.Validator = dtype.Build(param.Type, datatypes...) - param.ExtractType = dtype.Type() + param.Validator = dtype.Validator(param.Type, datatypes...) + param.GoType = dtype.GoType() if param.Validator != nil { break } diff --git a/internal/config/service.go b/internal/config/service.go index 3b051ba..6d10d27 100644 --- a/internal/config/service.go +++ b/internal/config/service.go @@ -6,7 +6,7 @@ import ( "regexp" "strings" - "github.com/xdrm-io/aicra/datatype" + "github.com/xdrm-io/aicra/validator" ) var braceRegex = regexp.MustCompile(`^{([a-z_-]+)}$`) @@ -97,7 +97,7 @@ func (svc *Service) matchPattern(uri string) bool { } // Validate implements the validator interface -func (svc *Service) validate(datatypes ...datatype.T) error { +func (svc *Service) validate(datatypes ...validator.Type) error { // check method err := svc.isMethodAvailable() if err != nil { @@ -195,7 +195,7 @@ func (svc *Service) isPatternValid() error { return nil } -func (svc *Service) validateInput(types []datatype.T) error { +func (svc *Service) validateInput(types []validator.Type) error { // ignore no parameter if svc.Input == nil || len(svc.Input) < 1 { @@ -285,7 +285,7 @@ func (svc *Service) validateInput(types []datatype.T) error { return nil } -func (svc *Service) validateOutput(types []datatype.T) error { +func (svc *Service) validateOutput(types []validator.Type) error { // ignore no parameter if svc.Output == nil || len(svc.Output) < 1 { diff --git a/internal/dynfunc/signature.go b/internal/dynfunc/signature.go index 35783ab..d5c30aa 100644 --- a/internal/dynfunc/signature.go +++ b/internal/dynfunc/signature.go @@ -31,17 +31,17 @@ func BuildSignature(service config.Service) *Signature { } // make a pointer if optional if param.Optional { - s.Input[param.Rename] = reflect.PtrTo(param.ExtractType) + s.Input[param.Rename] = reflect.PtrTo(param.GoType) continue } - s.Input[param.Rename] = param.ExtractType + s.Input[param.Rename] = param.GoType } for _, param := range service.Output { if len(param.Rename) < 1 { continue } - s.Output[param.Rename] = param.ExtractType + s.Output[param.Rename] = param.GoType } return s diff --git a/datatype/builtin/any.go b/validator/builtin/any.go similarity index 54% rename from datatype/builtin/any.go rename to validator/builtin/any.go index fdfd5f9..36a3e5a 100644 --- a/datatype/builtin/any.go +++ b/validator/builtin/any.go @@ -3,19 +3,19 @@ package builtin import ( "reflect" - "github.com/xdrm-io/aicra/datatype" + "github.com/xdrm-io/aicra/validator" ) // AnyDataType is what its name tells type AnyDataType struct{} -// Type returns the type of data -func (AnyDataType) Type() reflect.Type { +// GoType returns the type of data +func (AnyDataType) GoType() reflect.Type { return reflect.TypeOf(interface{}(nil)) } -// Build returns the validator -func (AnyDataType) Build(typeName string, registry ...datatype.T) datatype.Validator { +// Validator returns the validator +func (AnyDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { // nothing if type not handled if typeName != "any" { return nil diff --git a/datatype/builtin/any_test.go b/validator/builtin/any_test.go similarity index 87% rename from datatype/builtin/any_test.go rename to validator/builtin/any_test.go index 8d9ae03..ab7a681 100644 --- a/datatype/builtin/any_test.go +++ b/validator/builtin/any_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/xdrm-io/aicra/datatype/builtin" + "github.com/xdrm-io/aicra/validator/builtin" ) func TestAny_AvailableTypes(t *testing.T) { @@ -26,7 +26,7 @@ func TestAny_AvailableTypes(t *testing.T) { } for _, test := range tests { - validator := dt.Build(test.Type) + validator := dt.Validator(test.Type) if validator == nil { if test.Handled { @@ -47,7 +47,7 @@ func TestAny_AlwaysTrue(t *testing.T) { const typeName = "any" - validator := builtin.AnyDataType{}.Build(typeName) + validator := builtin.AnyDataType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() diff --git a/datatype/builtin/bool.go b/validator/builtin/bool.go similarity index 70% rename from datatype/builtin/bool.go rename to validator/builtin/bool.go index 720c59d..e5bd9df 100644 --- a/datatype/builtin/bool.go +++ b/validator/builtin/bool.go @@ -3,19 +3,19 @@ package builtin import ( "reflect" - "github.com/xdrm-io/aicra/datatype" + "github.com/xdrm-io/aicra/validator" ) // BoolDataType is what its name tells type BoolDataType struct{} -// Type returns the type of data -func (BoolDataType) Type() reflect.Type { +// GoType returns the type of data +func (BoolDataType) GoType() reflect.Type { return reflect.TypeOf(true) } -// Build returns the validator -func (BoolDataType) Build(typeName string, registry ...datatype.T) datatype.Validator { +// Validator returns the validator +func (BoolDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { // nothing if type not handled if typeName != "bool" { return nil diff --git a/datatype/builtin/bool_test.go b/validator/builtin/bool_test.go similarity index 91% rename from datatype/builtin/bool_test.go rename to validator/builtin/bool_test.go index c44b262..1d59ec9 100644 --- a/datatype/builtin/bool_test.go +++ b/validator/builtin/bool_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/xdrm-io/aicra/datatype/builtin" + "github.com/xdrm-io/aicra/validator/builtin" ) func TestBool_AvailableTypes(t *testing.T) { @@ -26,7 +26,7 @@ func TestBool_AvailableTypes(t *testing.T) { for _, test := range tests { t.Run(test.Type, func(t *testing.T) { - validator := dt.Build(test.Type) + validator := dt.Validator(test.Type) if validator == nil { if test.Handled { t.Errorf("expect %q to be handled", test.Type) @@ -49,7 +49,7 @@ func TestBool_Values(t *testing.T) { const typeName = "bool" - validator := builtin.BoolDataType{}.Build(typeName) + validator := builtin.BoolDataType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() diff --git a/datatype/builtin/float.go b/validator/builtin/float.go similarity index 75% rename from datatype/builtin/float.go rename to validator/builtin/float.go index 6bb39f9..0789444 100644 --- a/datatype/builtin/float.go +++ b/validator/builtin/float.go @@ -4,19 +4,19 @@ import ( "encoding/json" "reflect" - "github.com/xdrm-io/aicra/datatype" + "github.com/xdrm-io/aicra/validator" ) // FloatDataType is what its name tells type FloatDataType struct{} -// Type returns the type of data -func (FloatDataType) Type() reflect.Type { +// GoType returns the type of data +func (FloatDataType) GoType() reflect.Type { return reflect.TypeOf(float64(0)) } -// Build returns the validator -func (FloatDataType) Build(typeName string, registry ...datatype.T) datatype.Validator { +// Validator returns the validator +func (FloatDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { // nothing if type not handled if typeName != "float64" && typeName != "float" { return nil diff --git a/datatype/builtin/float_test.go b/validator/builtin/float_test.go similarity index 93% rename from datatype/builtin/float_test.go rename to validator/builtin/float_test.go index 193dd7c..924036e 100644 --- a/datatype/builtin/float_test.go +++ b/validator/builtin/float_test.go @@ -5,7 +5,7 @@ import ( "math" "testing" - "github.com/xdrm-io/aicra/datatype/builtin" + "github.com/xdrm-io/aicra/validator/builtin" ) func TestFloat64_AvailableTypes(t *testing.T) { @@ -33,7 +33,7 @@ func TestFloat64_AvailableTypes(t *testing.T) { for _, test := range tests { t.Run(test.Type, func(t *testing.T) { - validator := dt.Build(test.Type) + validator := dt.Validator(test.Type) if validator == nil { if test.Handled { t.Errorf("expect %q to be handled", test.Type) @@ -56,7 +56,7 @@ func TestFloat64_Values(t *testing.T) { const typeName = "float" - validator := builtin.FloatDataType{}.Build(typeName) + validator := builtin.FloatDataType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() diff --git a/datatype/builtin/int.go b/validator/builtin/int.go similarity index 79% rename from datatype/builtin/int.go rename to validator/builtin/int.go index b1473a6..1d0ad00 100644 --- a/datatype/builtin/int.go +++ b/validator/builtin/int.go @@ -5,19 +5,19 @@ import ( "math" "reflect" - "github.com/xdrm-io/aicra/datatype" + "github.com/xdrm-io/aicra/validator" ) // IntDataType is what its name tells type IntDataType struct{} -// Type returns the type of data -func (IntDataType) Type() reflect.Type { +// GoType returns the type of data +func (IntDataType) GoType() reflect.Type { return reflect.TypeOf(int(0)) } -// Build returns the validator -func (IntDataType) Build(typeName string, registry ...datatype.T) datatype.Validator { +// Validator returns the validator +func (IntDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { // nothing if type not handled if typeName != "int" { return nil diff --git a/datatype/builtin/int_test.go b/validator/builtin/int_test.go similarity index 93% rename from datatype/builtin/int_test.go rename to validator/builtin/int_test.go index 8b7a157..cffea8f 100644 --- a/datatype/builtin/int_test.go +++ b/validator/builtin/int_test.go @@ -5,7 +5,7 @@ import ( "math" "testing" - "github.com/xdrm-io/aicra/datatype/builtin" + "github.com/xdrm-io/aicra/validator/builtin" ) func TestInt_AvailableTypes(t *testing.T) { @@ -27,7 +27,7 @@ func TestInt_AvailableTypes(t *testing.T) { for _, test := range tests { t.Run(test.Type, func(t *testing.T) { - validator := dt.Build(test.Type) + validator := dt.Validator(test.Type) if validator == nil { if test.Handled { t.Errorf("expect %q to be handled", test.Type) @@ -50,7 +50,7 @@ func TestInt_Values(t *testing.T) { const typeName = "int" - validator := builtin.IntDataType{}.Build(typeName) + validator := builtin.IntDataType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() diff --git a/datatype/builtin/string.go b/validator/builtin/string.go similarity index 91% rename from datatype/builtin/string.go rename to validator/builtin/string.go index 902daf7..50544a7 100644 --- a/datatype/builtin/string.go +++ b/validator/builtin/string.go @@ -5,7 +5,7 @@ import ( "regexp" "strconv" - "github.com/xdrm-io/aicra/datatype" + "github.com/xdrm-io/aicra/validator" ) var fixedLengthRegex = regexp.MustCompile(`^string\((\d+)\)$`) @@ -14,14 +14,14 @@ var variableLengthRegex = regexp.MustCompile(`^string\((\d+), ?(\d+)\)$`) // StringDataType is what its name tells type StringDataType struct{} -// Type returns the type of data -func (StringDataType) Type() reflect.Type { +// GoType returns the type of data +func (StringDataType) GoType() reflect.Type { return reflect.TypeOf(string("")) } -// Build returns the validator. +// Validator returns the validator. // availables type names are : `string`, `string(length)` and `string(minLength, maxLength)`. -func (s StringDataType) Build(typeName string, registry ...datatype.T) datatype.Validator { +func (s StringDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { simple := typeName == "string" fixedLengthMatches := fixedLengthRegex.FindStringSubmatch(typeName) variableLengthMatches := variableLengthRegex.FindStringSubmatch(typeName) diff --git a/datatype/builtin/string_test.go b/validator/builtin/string_test.go similarity index 94% rename from datatype/builtin/string_test.go rename to validator/builtin/string_test.go index 4441525..7b91b2d 100644 --- a/datatype/builtin/string_test.go +++ b/validator/builtin/string_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/xdrm-io/aicra/datatype/builtin" + "github.com/xdrm-io/aicra/validator/builtin" ) func TestString_AvailableTypes(t *testing.T) { @@ -53,7 +53,7 @@ func TestString_AvailableTypes(t *testing.T) { for _, test := range tests { t.Run(test.Type, func(t *testing.T) { - validator := dt.Build(test.Type) + validator := dt.Validator(test.Type) if validator == nil { if test.Handled { @@ -75,7 +75,7 @@ func TestString_AnyLength(t *testing.T) { const typeName = "string" - validator := builtin.StringDataType{}.Build(typeName) + validator := builtin.StringDataType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() @@ -133,7 +133,7 @@ func TestString_FixedLength(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - validator := builtin.StringDataType{}.Build(test.Type) + validator := builtin.StringDataType{}.Validator(test.Type) if validator == nil { t.Errorf("expect %q to be handled", test.Type) t.Fail() @@ -194,7 +194,7 @@ func TestString_VariableLength(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - validator := builtin.StringDataType{}.Build(test.Type) + validator := builtin.StringDataType{}.Validator(test.Type) if validator == nil { t.Errorf("expect %q to be handled", test.Type) t.Fail() diff --git a/datatype/builtin/uint.go b/validator/builtin/uint.go similarity index 81% rename from datatype/builtin/uint.go rename to validator/builtin/uint.go index 990d15b..d6e25cf 100644 --- a/datatype/builtin/uint.go +++ b/validator/builtin/uint.go @@ -5,19 +5,19 @@ import ( "math" "reflect" - "github.com/xdrm-io/aicra/datatype" + "github.com/xdrm-io/aicra/validator" ) // UintDataType is what its name tells type UintDataType struct{} -// Type returns the type of data -func (UintDataType) Type() reflect.Type { +// GoType returns the type of data +func (UintDataType) GoType() reflect.Type { return reflect.TypeOf(uint(0)) } -// Build returns the validator -func (UintDataType) Build(typeName string, registry ...datatype.T) datatype.Validator { +// Validator returns the validator +func (UintDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { // nothing if type not handled if typeName != "uint" { return nil diff --git a/datatype/builtin/uint_test.go b/validator/builtin/uint_test.go similarity index 93% rename from datatype/builtin/uint_test.go rename to validator/builtin/uint_test.go index 6175321..9893264 100644 --- a/datatype/builtin/uint_test.go +++ b/validator/builtin/uint_test.go @@ -5,7 +5,7 @@ import ( "math" "testing" - "github.com/xdrm-io/aicra/datatype/builtin" + "github.com/xdrm-io/aicra/validator/builtin" ) func TestUint_AvailableTypes(t *testing.T) { @@ -27,7 +27,7 @@ func TestUint_AvailableTypes(t *testing.T) { for _, test := range tests { t.Run(test.Type, func(t *testing.T) { - validator := dt.Build(test.Type) + validator := dt.Validator(test.Type) if validator == nil { if test.Handled { t.Errorf("expect %q to be handled", test.Type) @@ -50,7 +50,7 @@ func TestUint_Values(t *testing.T) { const typeName = "uint" - validator := builtin.UintDataType{}.Build(typeName) + validator := builtin.UintDataType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() diff --git a/validator/validator.go b/validator/validator.go new file mode 100644 index 0000000..574f966 --- /dev/null +++ b/validator/validator.go @@ -0,0 +1,55 @@ +package validator + +import ( + "reflect" +) + +// ValidateFunc returns whether a given value fulfills the datatype and casts +// the value into a go type. +// +// for example, if a validator checks for upper case strings, whether the value +// is a []byte, a string or a []rune, if the value matches is all upper-case, it +// will be cast into a go type, say, string. +type ValidateFunc func(value interface{}) (cast interface{}, valid bool) + +// Type defines an available in/out parameter "type" for the aicra configuration +// +// A Type maps to a go type in order to generate the handler signature from the +// aicra configuration +// +// A Type returns a custom validator when the typename matches +type Type interface { + // Validator function when the typename matches. It must return nil when the + // typename does not match + // + // The `typename` argument has to match types used in your aicra configuration + // in parameter definitions ("in", "out") and in the "type" json field. + // + // basic example: + // - `IntType.Validator("string")`` should return nil + // - `IntType.Validator("int")`` should return its ValidateFunc + // + // The `typename` is not returned by a simple method i.e. `TypeName() string` + // because it allows for validation relative to the typename, for instance: + // - `VarcharType.Validator("varchar")` valides any string + // - `VarcharType.Validator("varchar(2)")` validates any string of 2 + // characters + // - `VarcharType.Validator("varchar(1,3)")` validates any string + // with a length between 1 and 3 + // + // The `registry` argument represents all other available Types. It allows a + // Type to use other available Types internally. + // + // recursive example: slices + // - `SliceType.Validator("[]int", reg...)` validates a slice containing + // values that are valide to the `IntType` + // - `SliceType.Validator("[]varchar", reg...)` validates a slice containing + // values that are valid to the `VarcharType` + // + // and so on.. this works for maps, structs, etc + Validator(typename string, registry ...Type) ValidateFunc + + // GoType must return the go type associated with the output type of ValidateFunc. + // It is used to define handlers' signature from the configuration file. + GoType() reflect.Type +} From de547576c93a070ab912939e95d98e322995a335 Mon Sep 17 00:00:00 2001 From: xdrm-brackets Date: Mon, 21 Jun 2021 21:30:33 +0200 Subject: [PATCH 2/2] refactor: semantic move 'builtin' into 'validator' --- README.md | 6 ++-- builder_test.go | 18 +++++------ handler_test.go | 14 ++++---- internal/config/config_test.go | 26 +++++++-------- validator/any.go | 24 ++++++++++++++ validator/{builtin => }/any_test.go | 8 ++--- validator/{builtin => }/bool.go | 23 ++++++------- validator/{builtin => }/bool_test.go | 8 ++--- validator/builtin/any.go | 26 --------------- validator/{builtin => }/float.go | 25 +++++++------- validator/{builtin => }/float_test.go | 8 ++--- validator/{builtin => }/int.go | 24 ++++++++------ validator/{builtin => }/int_test.go | 8 ++--- validator/{builtin => }/string.go | 45 ++++++++++++++------------ validator/{builtin => }/string_test.go | 12 +++---- validator/{builtin => }/uint.go | 25 +++++++------- validator/{builtin => }/uint_test.go | 8 ++--- validator/validator.go | 8 ++--- 18 files changed, 165 insertions(+), 151 deletions(-) create mode 100644 validator/any.go rename validator/{builtin => }/any_test.go (86%) rename validator/{builtin => }/bool.go (51%) rename validator/{builtin => }/bool_test.go (90%) delete mode 100644 validator/builtin/any.go rename validator/{builtin => }/float.go (52%) rename validator/{builtin => }/float_test.go (92%) rename validator/{builtin => }/int.go (63%) rename validator/{builtin => }/int_test.go (93%) rename validator/{builtin => }/string.go (63%) rename validator/{builtin => }/string_test.go (93%) rename validator/{builtin => }/uint.go (65%) rename validator/{builtin => }/uint_test.go (93%) diff --git a/README.md b/README.md index 467fbc1..d0dd2d2 100644 --- a/README.md +++ b/README.md @@ -96,9 +96,9 @@ func main() { builder := &aicra.Builder{} // add custom type validators - builder.Validate(builtin.BoolDataType{}) - builder.Validate(builtin.UintDataType{}) - builder.Validate(builtin.StringDataType{}) + builder.Validate(validator.BoolDataType{}) + builder.Validate(validator.UintDataType{}) + builder.Validate(validator.StringDataType{}) // load your configuration config, err := os.Open("api.json") diff --git a/builder_test.go b/builder_test.go index 457449b..fbbebe0 100644 --- a/builder_test.go +++ b/builder_test.go @@ -8,26 +8,26 @@ import ( "testing" "github.com/xdrm-io/aicra/api" - "github.com/xdrm-io/aicra/validator/builtin" + "github.com/xdrm-io/aicra/validator" ) func addBuiltinTypes(b *Builder) error { - if err := b.Validate(builtin.AnyDataType{}); err != nil { + if err := b.Validate(validator.AnyType{}); err != nil { return err } - if err := b.Validate(builtin.BoolDataType{}); err != nil { + if err := b.Validate(validator.BoolType{}); err != nil { return err } - if err := b.Validate(builtin.FloatDataType{}); err != nil { + if err := b.Validate(validator.FloatType{}); err != nil { return err } - if err := b.Validate(builtin.IntDataType{}); err != nil { + if err := b.Validate(validator.IntType{}); err != nil { return err } - if err := b.Validate(builtin.StringDataType{}); err != nil { + if err := b.Validate(validator.StringType{}); err != nil { return err } - if err := b.Validate(builtin.UintDataType{}); err != nil { + if err := b.Validate(validator.UintType{}); err != nil { return err } return nil @@ -35,7 +35,7 @@ func addBuiltinTypes(b *Builder) error { func TestAddType(t *testing.T) { builder := &Builder{} - err := builder.Validate(builtin.BoolDataType{}) + err := builder.Validate(validator.BoolType{}) if err != nil { t.Fatalf("unexpected error: %s", err) } @@ -43,7 +43,7 @@ func TestAddType(t *testing.T) { if err != nil { t.Fatalf("unexpected error: %s", err) } - err = builder.Validate(builtin.FloatDataType{}) + err = builder.Validate(validator.FloatType{}) if err != errLateType { t.Fatalf("expected <%v> got <%v>", errLateType, err) } diff --git a/handler_test.go b/handler_test.go index 51e9719..fe3327e 100644 --- a/handler_test.go +++ b/handler_test.go @@ -12,26 +12,26 @@ import ( "github.com/xdrm-io/aicra" "github.com/xdrm-io/aicra/api" - "github.com/xdrm-io/aicra/validator/builtin" + "github.com/xdrm-io/aicra/validator" ) func addBuiltinTypes(b *aicra.Builder) error { - if err := b.Validate(builtin.AnyDataType{}); err != nil { + if err := b.Validate(validator.AnyType{}); err != nil { return err } - if err := b.Validate(builtin.BoolDataType{}); err != nil { + if err := b.Validate(validator.BoolType{}); err != nil { return err } - if err := b.Validate(builtin.FloatDataType{}); err != nil { + if err := b.Validate(validator.FloatType{}); err != nil { return err } - if err := b.Validate(builtin.IntDataType{}); err != nil { + if err := b.Validate(validator.IntType{}); err != nil { return err } - if err := b.Validate(builtin.StringDataType{}); err != nil { + if err := b.Validate(validator.StringType{}); err != nil { return err } - if err := b.Validate(builtin.UintDataType{}); err != nil { + if err := b.Validate(validator.UintType{}); err != nil { return err } return nil diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 2bf019f..5db787c 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -8,7 +8,7 @@ import ( "strings" "testing" - "github.com/xdrm-io/aicra/validator/builtin" + "github.com/xdrm-io/aicra/validator" ) func TestLegalServiceName(t *testing.T) { @@ -239,7 +239,7 @@ func TestParamEmptyRenameNoRename(t *testing.T) { } ]`) srv := &Server{} - srv.Validators = append(srv.Validators, builtin.AnyDataType{}) + srv.Validators = append(srv.Validators, validator.AnyType{}) err := srv.Parse(r) if err != nil { t.Errorf("unexpected error: '%s'", err) @@ -275,8 +275,8 @@ func TestOptionalParam(t *testing.T) { } ]`) srv := &Server{} - srv.Validators = append(srv.Validators, builtin.AnyDataType{}) - srv.Validators = append(srv.Validators, builtin.BoolDataType{}) + srv.Validators = append(srv.Validators, validator.AnyType{}) + srv.Validators = append(srv.Validators, validator.BoolType{}) err := srv.Parse(r) if err != nil { t.Errorf("unexpected error: '%s'", err) @@ -588,7 +588,7 @@ func TestParseParameters(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { srv := &Server{} - srv.Validators = append(srv.Validators, builtin.AnyDataType{}) + srv.Validators = append(srv.Validators, validator.AnyType{}) err := srv.Parse(strings.NewReader(test.Raw)) if err == nil && test.Error != nil { @@ -827,8 +827,8 @@ func TestServiceCollision(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { srv := &Server{} - srv.Validators = append(srv.Validators, builtin.StringDataType{}) - srv.Validators = append(srv.Validators, builtin.UintDataType{}) + srv.Validators = append(srv.Validators, validator.StringType{}) + srv.Validators = append(srv.Validators, validator.UintType{}) err := srv.Parse(strings.NewReader(test.Config)) if err == nil && test.Error != nil { @@ -997,9 +997,9 @@ func TestMatchSimple(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { srv := &Server{} - srv.Validators = append(srv.Validators, builtin.AnyDataType{}) - srv.Validators = append(srv.Validators, builtin.IntDataType{}) - srv.Validators = append(srv.Validators, builtin.BoolDataType{}) + srv.Validators = append(srv.Validators, validator.AnyType{}) + srv.Validators = append(srv.Validators, validator.IntType{}) + srv.Validators = append(srv.Validators, validator.BoolType{}) err := srv.Parse(strings.NewReader(test.Config)) if err != nil { @@ -1081,9 +1081,9 @@ func TestFindPriority(t *testing.T) { t.Run(fmt.Sprintf("method.%d", i), func(t *testing.T) { srv := &Server{} - srv.Validators = append(srv.Validators, builtin.AnyDataType{}) - srv.Validators = append(srv.Validators, builtin.IntDataType{}) - srv.Validators = append(srv.Validators, builtin.BoolDataType{}) + srv.Validators = append(srv.Validators, validator.AnyType{}) + srv.Validators = append(srv.Validators, validator.IntType{}) + srv.Validators = append(srv.Validators, validator.BoolType{}) err := srv.Parse(strings.NewReader(test.Config)) if err != nil { diff --git a/validator/any.go b/validator/any.go new file mode 100644 index 0000000..c9b2ef0 --- /dev/null +++ b/validator/any.go @@ -0,0 +1,24 @@ +package validator + +import ( + "reflect" +) + +// AnyType makes the "any" type available in the aicra configuration +// It considers valid any value +type AnyType struct{} + +// GoType returns the interface{} type +func (AnyType) GoType() reflect.Type { + return reflect.TypeOf(interface{}(nil)) +} + +// Validator that considers any value valid +func (AnyType) Validator(typename string, avail ...Type) ValidateFunc { + if typename != "any" { + return nil + } + return func(value interface{}) (interface{}, bool) { + return value, true + } +} diff --git a/validator/builtin/any_test.go b/validator/any_test.go similarity index 86% rename from validator/builtin/any_test.go rename to validator/any_test.go index ab7a681..d612047 100644 --- a/validator/builtin/any_test.go +++ b/validator/any_test.go @@ -1,16 +1,16 @@ -package builtin_test +package validator_test import ( "fmt" "testing" - "github.com/xdrm-io/aicra/validator/builtin" + "github.com/xdrm-io/aicra/validator" ) func TestAny_AvailableTypes(t *testing.T) { t.Parallel() - dt := builtin.AnyDataType{} + dt := validator.AnyType{} tests := []struct { Type string @@ -47,7 +47,7 @@ func TestAny_AlwaysTrue(t *testing.T) { const typeName = "any" - validator := builtin.AnyDataType{}.Validator(typeName) + validator := validator.AnyType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() diff --git a/validator/builtin/bool.go b/validator/bool.go similarity index 51% rename from validator/builtin/bool.go rename to validator/bool.go index e5bd9df..9a93069 100644 --- a/validator/builtin/bool.go +++ b/validator/bool.go @@ -1,23 +1,24 @@ -package builtin +package validator import ( "reflect" - - "github.com/xdrm-io/aicra/validator" ) -// BoolDataType is what its name tells -type BoolDataType struct{} +// BoolType makes the "bool" type available in the aicra configuration +// It considers valid: +// - booleans +// - strings containing "true" or "false" +// - []byte containing "true" or "false" +type BoolType struct{} -// GoType returns the type of data -func (BoolDataType) GoType() reflect.Type { +// GoType returns the `bool` type +func (BoolType) GoType() reflect.Type { return reflect.TypeOf(true) } -// Validator returns the validator -func (BoolDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { - // nothing if type not handled - if typeName != "bool" { +// Validator for bool values +func (BoolType) Validator(typename string, avail ...Type) ValidateFunc { + if typename != "bool" { return nil } diff --git a/validator/builtin/bool_test.go b/validator/bool_test.go similarity index 90% rename from validator/builtin/bool_test.go rename to validator/bool_test.go index 1d59ec9..8600d34 100644 --- a/validator/builtin/bool_test.go +++ b/validator/bool_test.go @@ -1,16 +1,16 @@ -package builtin_test +package validator_test import ( "fmt" "testing" - "github.com/xdrm-io/aicra/validator/builtin" + "github.com/xdrm-io/aicra/validator" ) func TestBool_AvailableTypes(t *testing.T) { t.Parallel() - dt := builtin.BoolDataType{} + dt := validator.BoolType{} tests := []struct { Type string @@ -49,7 +49,7 @@ func TestBool_Values(t *testing.T) { const typeName = "bool" - validator := builtin.BoolDataType{}.Validator(typeName) + validator := validator.BoolType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() diff --git a/validator/builtin/any.go b/validator/builtin/any.go deleted file mode 100644 index 36a3e5a..0000000 --- a/validator/builtin/any.go +++ /dev/null @@ -1,26 +0,0 @@ -package builtin - -import ( - "reflect" - - "github.com/xdrm-io/aicra/validator" -) - -// AnyDataType is what its name tells -type AnyDataType struct{} - -// GoType returns the type of data -func (AnyDataType) GoType() reflect.Type { - return reflect.TypeOf(interface{}(nil)) -} - -// Validator returns the validator -func (AnyDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { - // nothing if type not handled - if typeName != "any" { - return nil - } - return func(value interface{}) (interface{}, bool) { - return value, true - } -} diff --git a/validator/builtin/float.go b/validator/float.go similarity index 52% rename from validator/builtin/float.go rename to validator/float.go index 0789444..b6d7e53 100644 --- a/validator/builtin/float.go +++ b/validator/float.go @@ -1,24 +1,27 @@ -package builtin +package validator import ( "encoding/json" "reflect" - - "github.com/xdrm-io/aicra/validator" ) -// FloatDataType is what its name tells -type FloatDataType struct{} +// FloatType makes the "float" (or "float64") type available in the aicra configuration +// It considers valid: +// - float64 +// - int (since it does not overflow) +// - uint (since it does not overflow) +// - strings containing json-compatible floats +// - []byte containing json-compatible floats +type FloatType struct{} -// GoType returns the type of data -func (FloatDataType) GoType() reflect.Type { +// GoType returns the `float64` type +func (FloatType) GoType() reflect.Type { return reflect.TypeOf(float64(0)) } -// Validator returns the validator -func (FloatDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { - // nothing if type not handled - if typeName != "float64" && typeName != "float" { +// Validator for float64 values +func (FloatType) Validator(typename string, avail ...Type) ValidateFunc { + if typename != "float64" && typename != "float" { return nil } return func(value interface{}) (interface{}, bool) { diff --git a/validator/builtin/float_test.go b/validator/float_test.go similarity index 92% rename from validator/builtin/float_test.go rename to validator/float_test.go index 924036e..b965441 100644 --- a/validator/builtin/float_test.go +++ b/validator/float_test.go @@ -1,17 +1,17 @@ -package builtin_test +package validator_test import ( "fmt" "math" "testing" - "github.com/xdrm-io/aicra/validator/builtin" + "github.com/xdrm-io/aicra/validator" ) func TestFloat64_AvailableTypes(t *testing.T) { t.Parallel() - dt := builtin.FloatDataType{} + dt := validator.FloatType{} tests := []struct { Type string @@ -56,7 +56,7 @@ func TestFloat64_Values(t *testing.T) { const typeName = "float" - validator := builtin.FloatDataType{}.Validator(typeName) + validator := validator.FloatType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() diff --git a/validator/builtin/int.go b/validator/int.go similarity index 63% rename from validator/builtin/int.go rename to validator/int.go index 1d0ad00..d7ec83c 100644 --- a/validator/builtin/int.go +++ b/validator/int.go @@ -1,25 +1,29 @@ -package builtin +package validator import ( "encoding/json" "math" "reflect" - - "github.com/xdrm-io/aicra/validator" ) -// IntDataType is what its name tells -type IntDataType struct{} +// IntType makes the "int" type available in the aicra configuration +// It considers valid: +// - int +// - float64 (since it does not overflow) +// - uint (since it does not overflow) +// - strings containing json-compatible integers +// - []byte containing json-compatible integers +type IntType struct{} -// GoType returns the type of data -func (IntDataType) GoType() reflect.Type { +// GoType returns the `int` type +func (IntType) GoType() reflect.Type { return reflect.TypeOf(int(0)) } -// Validator returns the validator -func (IntDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { +// Validator for int values +func (IntType) Validator(typename string, avail ...Type) ValidateFunc { // nothing if type not handled - if typeName != "int" { + if typename != "int" { return nil } diff --git a/validator/builtin/int_test.go b/validator/int_test.go similarity index 93% rename from validator/builtin/int_test.go rename to validator/int_test.go index cffea8f..14dc515 100644 --- a/validator/builtin/int_test.go +++ b/validator/int_test.go @@ -1,17 +1,17 @@ -package builtin_test +package validator_test import ( "fmt" "math" "testing" - "github.com/xdrm-io/aicra/validator/builtin" + "github.com/xdrm-io/aicra/validator" ) func TestInt_AvailableTypes(t *testing.T) { t.Parallel() - dt := builtin.IntDataType{} + dt := validator.IntType{} tests := []struct { Type string @@ -50,7 +50,7 @@ func TestInt_Values(t *testing.T) { const typeName = "int" - validator := builtin.IntDataType{}.Validator(typeName) + validator := validator.IntType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() diff --git a/validator/builtin/string.go b/validator/string.go similarity index 63% rename from validator/builtin/string.go rename to validator/string.go index 50544a7..307c7cd 100644 --- a/validator/builtin/string.go +++ b/validator/string.go @@ -1,32 +1,37 @@ -package builtin +package validator import ( "reflect" "regexp" "strconv" - - "github.com/xdrm-io/aicra/validator" ) -var fixedLengthRegex = regexp.MustCompile(`^string\((\d+)\)$`) -var variableLengthRegex = regexp.MustCompile(`^string\((\d+), ?(\d+)\)$`) +var ( + fixedLengthRegex = regexp.MustCompile(`^string\((\d+)\)$`) + variableLengthRegex = regexp.MustCompile(`^string\((\d+), ?(\d+)\)$`) +) -// StringDataType is what its name tells -type StringDataType struct{} +// StringType makes the types beloz available in the aicra configuration: +// - "string" considers any string valid +// - "string(n)" considers any string with an exact size of `n` valid +// - "string(a,b)" considers any string with a size between `a` and `b` valid +// > for the last one, `a` and `b` are included in the valid sizes +type StringType struct{} -// GoType returns the type of data -func (StringDataType) GoType() reflect.Type { +// GoType returns the `string` type +func (StringType) GoType() reflect.Type { return reflect.TypeOf(string("")) } -// Validator returns the validator. -// availables type names are : `string`, `string(length)` and `string(minLength, maxLength)`. -func (s StringDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { - simple := typeName == "string" - fixedLengthMatches := fixedLengthRegex.FindStringSubmatch(typeName) - variableLengthMatches := variableLengthRegex.FindStringSubmatch(typeName) +// Validator for strings with any/fixed/bound sizes +func (s StringType) Validator(typename string, avail ...Type) ValidateFunc { + var ( + simple = (typename == "string") + fixedLengthMatches = fixedLengthRegex.FindStringSubmatch(typename) + variableLengthMatches = variableLengthRegex.FindStringSubmatch(typename) + ) - // nothing if type not handled + // ignore unknown typename if !simple && fixedLengthMatches == nil && variableLengthMatches == nil { return nil } @@ -40,7 +45,7 @@ func (s StringDataType) Validator(typeName string, registry ...validator.Type) v if fixedLengthMatches != nil { exLen, ok := s.getFixedLength(fixedLengthMatches) if !ok { - mustFail = true + return nil } min = exLen max = exLen @@ -49,7 +54,7 @@ func (s StringDataType) Validator(typeName string, registry ...validator.Type) v } else if variableLengthMatches != nil { exMin, exMax, ok := s.getVariableLength(variableLengthMatches) if !ok { - mustFail = true + return nil } min = exMin max = exMax @@ -84,7 +89,7 @@ func (s StringDataType) Validator(typeName string, registry ...validator.Type) v } // getFixedLength returns the fixed length from regex matches and a success state. -func (StringDataType) getFixedLength(regexMatches []string) (int, bool) { +func (StringType) getFixedLength(regexMatches []string) (int, bool) { // incoherence error if regexMatches == nil || len(regexMatches) < 2 { return 0, false @@ -100,7 +105,7 @@ func (StringDataType) getFixedLength(regexMatches []string) (int, bool) { } // getVariableLength returns the length min and max from regex matches and a success state. -func (StringDataType) getVariableLength(regexMatches []string) (int, int, bool) { +func (StringType) getVariableLength(regexMatches []string) (int, int, bool) { // incoherence error if regexMatches == nil || len(regexMatches) < 3 { return 0, 0, false diff --git a/validator/builtin/string_test.go b/validator/string_test.go similarity index 93% rename from validator/builtin/string_test.go rename to validator/string_test.go index 7b91b2d..0a038ef 100644 --- a/validator/builtin/string_test.go +++ b/validator/string_test.go @@ -1,16 +1,16 @@ -package builtin_test +package validator_test import ( "fmt" "testing" - "github.com/xdrm-io/aicra/validator/builtin" + "github.com/xdrm-io/aicra/validator" ) func TestString_AvailableTypes(t *testing.T) { t.Parallel() - dt := builtin.StringDataType{} + dt := validator.StringType{} tests := []struct { Type string @@ -75,7 +75,7 @@ func TestString_AnyLength(t *testing.T) { const typeName = "string" - validator := builtin.StringDataType{}.Validator(typeName) + validator := validator.StringType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() @@ -133,7 +133,7 @@ func TestString_FixedLength(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - validator := builtin.StringDataType{}.Validator(test.Type) + validator := validator.StringType{}.Validator(test.Type) if validator == nil { t.Errorf("expect %q to be handled", test.Type) t.Fail() @@ -194,7 +194,7 @@ func TestString_VariableLength(t *testing.T) { for i, test := range tests { t.Run(fmt.Sprintf("%d", i), func(t *testing.T) { - validator := builtin.StringDataType{}.Validator(test.Type) + validator := validator.StringType{}.Validator(test.Type) if validator == nil { t.Errorf("expect %q to be handled", test.Type) t.Fail() diff --git a/validator/builtin/uint.go b/validator/uint.go similarity index 65% rename from validator/builtin/uint.go rename to validator/uint.go index d6e25cf..0df72f4 100644 --- a/validator/builtin/uint.go +++ b/validator/uint.go @@ -1,25 +1,28 @@ -package builtin +package validator import ( "encoding/json" "math" "reflect" - - "github.com/xdrm-io/aicra/validator" ) -// UintDataType is what its name tells -type UintDataType struct{} +// UintType makes the "uint" type available in the aicra configuration +// It considers valid: +// - uint +// - int (since it does not overflow) +// - float64 (since it does not overflow) +// - strings containing json-compatible integers +// - []byte containing json-compatible integers +type UintType struct{} -// GoType returns the type of data -func (UintDataType) GoType() reflect.Type { +// GoType returns the `uint` type +func (UintType) GoType() reflect.Type { return reflect.TypeOf(uint(0)) } -// Validator returns the validator -func (UintDataType) Validator(typeName string, registry ...validator.Type) validator.ValidateFunc { - // nothing if type not handled - if typeName != "uint" { +// Validator for uint values +func (UintType) Validator(other string, avail ...Type) ValidateFunc { + if other != "uint" { return nil } diff --git a/validator/builtin/uint_test.go b/validator/uint_test.go similarity index 93% rename from validator/builtin/uint_test.go rename to validator/uint_test.go index 9893264..4fc9be9 100644 --- a/validator/builtin/uint_test.go +++ b/validator/uint_test.go @@ -1,17 +1,17 @@ -package builtin_test +package validator_test import ( "fmt" "math" "testing" - "github.com/xdrm-io/aicra/validator/builtin" + "github.com/xdrm-io/aicra/validator" ) func TestUint_AvailableTypes(t *testing.T) { t.Parallel() - dt := builtin.UintDataType{} + dt := validator.UintType{} tests := []struct { Type string @@ -50,7 +50,7 @@ func TestUint_Values(t *testing.T) { const typeName = "uint" - validator := builtin.UintDataType{}.Validator(typeName) + validator := validator.UintType{}.Validator(typeName) if validator == nil { t.Errorf("expect %q to be handled", typeName) t.Fail() diff --git a/validator/validator.go b/validator/validator.go index 574f966..fa3d389 100644 --- a/validator/validator.go +++ b/validator/validator.go @@ -37,17 +37,17 @@ type Type interface { // - `VarcharType.Validator("varchar(1,3)")` validates any string // with a length between 1 and 3 // - // The `registry` argument represents all other available Types. It allows a + // The `avail` argument represents all other available Types. It allows a // Type to use other available Types internally. // // recursive example: slices - // - `SliceType.Validator("[]int", reg...)` validates a slice containing + // - `SliceType.Validator("[]int", avail...)` validates a slice containing // values that are valide to the `IntType` - // - `SliceType.Validator("[]varchar", reg...)` validates a slice containing + // - `SliceType.Validator("[]varchar", avail...)` validates a slice containing // values that are valid to the `VarcharType` // // and so on.. this works for maps, structs, etc - Validator(typename string, registry ...Type) ValidateFunc + Validator(typename string, avail ...Type) ValidateFunc // GoType must return the go type associated with the output type of ValidateFunc. // It is used to define handlers' signature from the configuration file.