ref 0: internal.checker defines an interface ; default types are in internal.checker.builtin
This commit is contained in:
parent
8109f57d15
commit
8d45d3241b
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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')
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue