ref 0: internal.checker defines an interface ; default types are in internal.checker.builtin

This commit is contained in:
Adrien Marquès 2019-05-01 11:23:05 +02:00
parent 8109f57d15
commit 8d45d3241b
16 changed files with 240 additions and 382 deletions

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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')
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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
}

44
internal/checker/set.go Normal file
View File

@ -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
}

18
internal/checker/type.go Normal file
View File

@ -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
}

View File

@ -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