diff --git a/internal/checker/builtin/any.go b/internal/checker/builtin/any.go new file mode 100644 index 0000000..5f0352b --- /dev/null +++ b/internal/checker/builtin/any.go @@ -0,0 +1,24 @@ +package builtin + +import ( + "git.xdrm.io/go/aicra/internal/checker" +) + +// 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) checker.Checker { + // nothing if type not handled + if typeName != "any" { + return nil + } + return func(interface{}) bool { + return true + } +} diff --git a/internal/checker/builtin/bool.go b/internal/checker/builtin/bool.go new file mode 100644 index 0000000..90caa19 --- /dev/null +++ b/internal/checker/builtin/bool.go @@ -0,0 +1,23 @@ +package builtin + +import "git.xdrm.io/go/aicra/internal/checker" + +// 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) checker.Checker { + // nothing if type not handled + if typeName != "bool" { + return nil + } + return func(value interface{}) bool { + _, isBool := value.(bool) + return isBool + } +} diff --git a/internal/checker/builtin/float64.go b/internal/checker/builtin/float64.go new file mode 100644 index 0000000..9ff3dea --- /dev/null +++ b/internal/checker/builtin/float64.go @@ -0,0 +1,23 @@ +package builtin + +import "git.xdrm.io/go/aicra/internal/checker" + +// 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) checker.Checker { + // nothing if type not handled + if typeName != "float64" && typeName != "float" { + return nil + } + return func(value interface{}) bool { + _, isFloat64 := value.(bool) + return isFloat64 + } +} diff --git a/internal/checker/builtin/string.go b/internal/checker/builtin/string.go new file mode 100644 index 0000000..e82e5ea --- /dev/null +++ b/internal/checker/builtin/string.go @@ -0,0 +1,108 @@ +package builtin + +import ( + "regexp" + "strconv" + + "git.xdrm.io/go/aicra/internal/checker" +) + +var fixedLengthRegex = regexp.MustCompile(`^string\((\d+))$`) +var variableLengthRegex = regexp.MustCompile(`^string\((\d+), ?(\d+)\)$`) + +// String checks if a value is a string +type String 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) checker.Checker { + isSimpleString := typeName == "string" + fixedLengthMatches := fixedLengthRegex.FindStringSubmatch(typeName) + variableLengthMatches := variableLengthRegex.FindStringSubmatch(typeName) + + // nothing if type not handled + if !isSimpleString && fixedLengthMatches == nil && variableLengthMatches == nil { + return nil + } + + return func(value interface{}) bool { + // check type + strValue, isString := value.(string) + if !isString { + return false + } + + // check fixed length + if fixedLengthMatches != nil { + // incoherence fail + if len(fixedLengthMatches) < 2 { + return false + } + + // extract length + fixedLen, err := strconv.ParseUint(fixedLengthMatches[1], 10, 64) + if err != nil { + return false + } + + // check against value + return len(strValue) == int(fixedLen) + } + + // check variable length + if variableLengthMatches != nil { + + minLen, maxLen, ok := s.getVariableLength(variableLengthMatches) + if !ok { + return false + } + + // check against value + return len(strValue) >= minLen && len(strValue) <= maxLen + } + + // if should NEVER be here ; so fail + return false + } +} + +// getFixedLength returns the fixed length from regex matches and a success state. +func (String) getFixedLength(regexMatches []string) (int, bool) { + // incoherence error + if regexMatches == nil || len(regexMatches) < 2 { + return 0, false + } + + // extract length + fixedLength, err := strconv.ParseUint(regexMatches[1], 10, 64) + if err != nil { + return 0, false + } + + return int(fixedLength), true +} + +// getVariableLength returns the length min and max from regex matches and a success state. +func (String) getVariableLength(regexMatches []string) (int, int, bool) { + // incoherence error + if regexMatches == nil || len(regexMatches) < 3 { + return 0, 0, false + } + + // extract minimum length + minLen, err := strconv.ParseUint(regexMatches[1], 10, 64) + if err != nil { + return 0, 0, false + } + // extract maximum length + maxLen, err := strconv.ParseUint(regexMatches[2], 10, 64) + if err != nil { + return 0, 0, false + } + + return int(minLen), int(maxLen), true +} diff --git a/internal/checker/default/any/main.go b/internal/checker/default/any/main.go deleted file mode 100644 index ae178ee..0000000 --- a/internal/checker/default/any/main.go +++ /dev/null @@ -1,20 +0,0 @@ -package main - -import ( - "git.xdrm.io/go/aicra/driver" -) - -func main() {} -func Export() driver.Checker { return new(AnyChecker) } - -type AnyChecker int - -// Match matches the string 'any' -func (ack AnyChecker) Match(name string) bool { - return name == "any" -} - -// Check always returns true -func (ack AnyChecker) Check(value interface{}) bool { - return true -} diff --git a/internal/checker/default/bool/main.go b/internal/checker/default/bool/main.go deleted file mode 100644 index b5fe2ba..0000000 --- a/internal/checker/default/bool/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "git.xdrm.io/go/aicra/driver" -) - -func main() {} -func Export() driver.Checker { return new(BoolChecker) } - -type BoolChecker int - -// Match matches the string 'bool' -func (bck BoolChecker) Match(name string) bool { - return name == "bool" -} - -// Check returns true for any type from the @validationTable -func (bck BoolChecker) Check(value interface{}) bool { - - // check if bool - _, ok := value.(bool) - return ok - -} diff --git a/internal/checker/default/digest/main.go b/internal/checker/default/digest/main.go deleted file mode 100644 index 14b6c45..0000000 --- a/internal/checker/default/digest/main.go +++ /dev/null @@ -1,73 +0,0 @@ -package main - -import ( - "git.xdrm.io/go/aicra/driver" - "strconv" - "strings" -) - -func main() {} -func Export() driver.Checker { return new(DigestChecker) } - -type DigestChecker struct { - Length *uint64 -} - -// Match filters the parameter type format "varchar(min, max)" -func (dck *DigestChecker) Match(name string) bool { - - dck.Length = nil - - /* (1) Check prefix/suffix */ - if len(name) < len("digest(x)") || !strings.HasPrefix(name, "digest(") || name[len(name)-1] != ')' { - return false - } - - /* (2) Extract length */ - lengthStr := name[len("digest(") : len(name)-1] - - length, err := strconv.ParseUint(lengthStr, 10, 64) - if err != nil { - return false - } - dck.Length = &length - - return true - -} - -// Check whether the given value fulfills the condition (min, max) -func (dck *DigestChecker) Check(value interface{}) bool { - - /* (1) Check if sizes set */ - if dck.Length == nil { - return false - } - - /* (2) Check if string */ - strval, ok := value.(string) - if !ok { - return false - } - - length := uint64(len(strval)) - - /* (3) Check length */ - if length != *dck.Length { - return false - } - - /* (4) Check character set */ - for _, char := range strval { - if !isValidCharacter(char) { - return false - } - } - - return true - -} - -func isValidCharacter(char rune) bool { - return (char >= '0' && char <= '9') || (char >= 'a' && char <= 'f') -} diff --git a/internal/checker/default/float/main.go b/internal/checker/default/float/main.go deleted file mode 100644 index 3b272e8..0000000 --- a/internal/checker/default/float/main.go +++ /dev/null @@ -1,24 +0,0 @@ -package main - -import ( - "git.xdrm.io/go/aicra/driver" -) - -func main() {} -func Export() driver.Checker { return new(FloatChecker) } - -type FloatChecker int - -// Match matches the string 'int' -func (fck FloatChecker) Match(name string) bool { - return name == "float" -} - -// Check returns true for any type from the @validationTable -func (fck FloatChecker) Check(value interface{}) bool { - - // check if float (default wrapping type) - _, ok := value.(float64) - return ok - -} diff --git a/internal/checker/default/id/main.go b/internal/checker/default/id/main.go deleted file mode 100644 index 62df3ed..0000000 --- a/internal/checker/default/id/main.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "git.xdrm.io/go/aicra/driver" - "math" -) - -func main() {} -func Export() driver.Checker { return new(IdChecker) } - -type IdChecker int - -// Match matches the string 'id' -func (ick IdChecker) Match(name string) bool { - return name == "id" -} - -// Check returns true for any type from the @validationTable -func (ick IdChecker) Check(value interface{}) bool { - - // check if float (default wrapping type) - floatVal, ok := value.(float64) - if !ok { - return false - } - - // check if there is no floating point - return floatVal == math.Floor(floatVal) && floatVal > 0 - -} diff --git a/internal/checker/default/int/main.go b/internal/checker/default/int/main.go deleted file mode 100644 index 859e4b0..0000000 --- a/internal/checker/default/int/main.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "git.xdrm.io/go/aicra/driver" - "math" -) - -func main() {} -func Export() driver.Checker { return new(IntChecker) } - -type IntChecker int - -// Match matches the string 'int' -func (ick IntChecker) Match(name string) bool { - return name == "int" -} - -// Check returns true for any type from the @validationTable -func (ick IntChecker) Check(value interface{}) bool { - - // check if float (default wrapping type) - floatVal, ok := value.(float64) - if !ok { - return false - } - - // check if there is no floating point - return floatVal == math.Floor(floatVal) - -} diff --git a/internal/checker/default/string/main.go b/internal/checker/default/string/main.go deleted file mode 100644 index 78d62a3..0000000 --- a/internal/checker/default/string/main.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "git.xdrm.io/go/aicra/driver" -) - -func main() {} -func Export() driver.Checker { return new(StringChecker) } - -type StringChecker int - -func (sck StringChecker) Match(name string) bool { - return name == "string" -} - -func (sck StringChecker) Check(value interface{}) bool { - - if value == nil { - return false - } - - _, ok := value.(string) - - return ok - -} diff --git a/internal/checker/default/varchar/main.go b/internal/checker/default/varchar/main.go deleted file mode 100644 index 5c09876..0000000 --- a/internal/checker/default/varchar/main.go +++ /dev/null @@ -1,86 +0,0 @@ -package main - -import ( - "git.xdrm.io/go/aicra/driver" - "regexp" - "strconv" -) - -func main() {} -func Export() driver.Checker { return new(VarcharChecker) } - -type VarcharChecker struct { - min *uint64 - max *uint64 -} - -// Match filters the parameter type format "varchar(min, max)" -func (vck *VarcharChecker) Match(name string) bool { - - vck.min = nil - vck.max = nil - - /* (1) Create regexp */ - re, err := regexp.Compile(`^varchar\((\d+), ?(\d+)\)$`) - if err != nil { - panic(err) - } - - /* (2) Check if matches */ - matches := re.FindStringSubmatch(name) - if matches == nil || len(matches) < 3 { - return false - } - - /* (3) Extract min */ - minVal, err := strconv.ParseUint(matches[1], 10, 64) - if err != nil { - return false - } - vck.min = &minVal - - /* (4) Extract max */ - maxVal, err := strconv.ParseUint(matches[2], 10, 64) - if err != nil { - return false - } - - /* (5) Check that min <= max */ - if maxVal < minVal { - panic("varchar(x, y) ; constraint violation : x <= y") - } - vck.max = &maxVal - - return true - -} - -// Check whether the given value fulfills the condition (min, max) -func (vck *VarcharChecker) Check(value interface{}) bool { - - /* (1) Check if string */ - strval, ok := value.(string) - if !ok { - return false - } - - /* (2) Check if sizes set */ - if vck.min == nil || vck.max == nil { - return false - } - - length := uint64(len(strval)) - - /* (3) Check min */ - if length < *vck.min { - return false - } - - /* (4) Check max */ - if length > *vck.max { - return false - } - - return true - -} diff --git a/internal/checker/public.go b/internal/checker/public.go deleted file mode 100644 index d10960a..0000000 --- a/internal/checker/public.go +++ /dev/null @@ -1,52 +0,0 @@ -package checker - -import ( - "errors" - "git.xdrm.io/go/aicra/driver" -) - -// ErrNoMatchingType is returned when the Match() method does not find any type checker -var ErrNoMatchingType = errors.New("no matching type") - -// ErrDoesNotMatch is returned when the Check() method fails (invalid type value) -var ErrDoesNotMatch = errors.New("does not match") - -// CreateRegistry creates an empty type registry -func CreateRegistry() Registry { - return make(Registry) -} - -// Add adds a new checker for a path -func (reg Registry) Add(_path string, _element driver.Checker) { - reg[_path] = _element -} - -// Run finds a type checker from the registry matching the type @typeName -// and uses this checker to check the @value. If no type checker matches -// the @typeName name, error is returned by default. -func (reg Registry) Run(typeName string, value interface{}) error { - - /* (1) Iterate to find matching type (take first) */ - for _, t := range reg { - - if t == nil { - continue - } - - // stop if found - if t.Match(typeName) { - - // check value - if t.Check(value) { - return nil - } - - return ErrDoesNotMatch - - } - - } - - return ErrNoMatchingType - -} diff --git a/internal/checker/set.go b/internal/checker/set.go new file mode 100644 index 0000000..c9d5090 --- /dev/null +++ b/internal/checker/set.go @@ -0,0 +1,44 @@ +package checker + +// Set of type checkers +type Set struct { + types []Type +} + +// New returns a new set of type checkers +func New() *Set { + return &Set{types: make([]Type, 0)} +} + +// Add adds a new type checker +func (s *Set) Add(typeChecker Type) { + s.types = append(s.types, typeChecker) +} + +// Run finds a type checker from the registry matching the type `typeName` +// and uses this checker to check the `value`. If no type checker matches +// the `type`, error is returned by default. +func (s *Set) Run(typeName string, value interface{}) error { + + // find matching type (take first) + for _, typeChecker := range s.types { + if typeChecker == nil { + continue + } + + // found + checkerFunc := typeChecker.Checker(typeName) + if checkerFunc == nil { + continue + } + + // check value + if checkerFunc(value) { + return nil + } else { + return ErrDoesNotMatch + } + } + + return ErrNoMatchingType +} diff --git a/internal/checker/type.go b/internal/checker/type.go new file mode 100644 index 0000000..e5b0747 --- /dev/null +++ b/internal/checker/type.go @@ -0,0 +1,18 @@ +package checker + +import "errors" + +// ErrNoMatchingType when no available type checker matches the type +var ErrNoMatchingType = errors.New("no matching type") + +// ErrDoesNotMatch when the value is invalid +var ErrDoesNotMatch = errors.New("does not match") + +// Checker returns whether a given value fulfills a type +type Checker func(interface{}) bool + +// Type represents a type checker +type Type interface { + // given a type name, returns the checker function or NIL if the type is not handled here + Checker(string) Checker +} diff --git a/internal/checker/types.go b/internal/checker/types.go deleted file mode 100644 index e506851..0000000 --- a/internal/checker/types.go +++ /dev/null @@ -1,17 +0,0 @@ -package checker - -import ( - "git.xdrm.io/go/aicra/driver" -) - -// Matcher returns whether a type 'name' matches a type -type Matcher func(name string) bool - -// Checker returns whether 'value' is valid to this Type -// note: it is a pointer because it can be formatted by the checker if matches -// to provide indulgent type check if needed -type Checker func(value interface{}) bool - -// Registry represents a registry containing all available -// Type-s to be used by the framework according to the configuration -type Registry map[string]driver.Checker