feat: add builtin types [int, uint] | fix request param getters | add request param getters [GetFloat, GetInt, GetUint, GetStrings] with permissive possible types and 'automatic' conversion
This commit is contained in:
parent
eb5ce4c0d0
commit
a83d077569
|
@ -1,5 +1,9 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ConstError is a wrapper to set constant errors
|
||||
type ConstError string
|
||||
|
||||
|
@ -26,17 +30,133 @@ func (rp RequestParam) Get(key string) (interface{}, error) {
|
|||
return rawValue, nil
|
||||
}
|
||||
|
||||
// GetString returns a string and an error if not found or string
|
||||
// GetString returns a string and an error if not found or invalid type
|
||||
func (rp RequestParam) GetString(key string) (string, error) {
|
||||
rawValue, err := rp.Get(key)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
convertedValue, canConvert := rawValue.(string)
|
||||
if !canConvert {
|
||||
switch cast := rawValue.(type) {
|
||||
case fmt.Stringer:
|
||||
return cast.String(), nil
|
||||
case []byte:
|
||||
return string(cast), nil
|
||||
case string:
|
||||
return cast, nil
|
||||
default:
|
||||
return "", ErrReqParamNotType
|
||||
}
|
||||
|
||||
return convertedValue, nil
|
||||
}
|
||||
|
||||
// GetFloat returns a float64 and an error if not found or invalid type
|
||||
func (rp RequestParam) GetFloat(key string) (float64, error) {
|
||||
rawValue, err := rp.Get(key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch cast := rawValue.(type) {
|
||||
case float32:
|
||||
return float64(cast), nil
|
||||
case float64:
|
||||
return cast, nil
|
||||
case int, int8, int16, int32, int64:
|
||||
intVal, ok := cast.(int)
|
||||
if !ok || intVal != int(float64(intVal)) {
|
||||
return 0, ErrReqParamNotType
|
||||
}
|
||||
return float64(intVal), nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
uintVal, ok := cast.(uint)
|
||||
if !ok || uintVal != uint(float64(uintVal)) {
|
||||
return 0, ErrReqParamNotType
|
||||
}
|
||||
return float64(uintVal), nil
|
||||
default:
|
||||
return 0, ErrReqParamNotType
|
||||
}
|
||||
}
|
||||
|
||||
// GetInt returns an int and an error if not found or invalid type
|
||||
func (rp RequestParam) GetInt(key string) (int, error) {
|
||||
rawValue, err := rp.Get(key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch cast := rawValue.(type) {
|
||||
case float32, float64:
|
||||
floatVal, ok := cast.(float64)
|
||||
if !ok || floatVal < 0 || floatVal != float64(int(floatVal)) {
|
||||
return 0, ErrReqParamNotType
|
||||
}
|
||||
return int(floatVal), nil
|
||||
case int, int8, int16, int32, int64:
|
||||
intVal, ok := cast.(int)
|
||||
if !ok || intVal != int(int(intVal)) {
|
||||
return 0, ErrReqParamNotType
|
||||
}
|
||||
return int(intVal), nil
|
||||
default:
|
||||
return 0, ErrReqParamNotType
|
||||
}
|
||||
}
|
||||
|
||||
// GetUint returns an uint and an error if not found or invalid type
|
||||
func (rp RequestParam) GetUint(key string) (uint, error) {
|
||||
rawValue, err := rp.Get(key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch cast := rawValue.(type) {
|
||||
case float32, float64:
|
||||
floatVal, ok := cast.(float64)
|
||||
if !ok || floatVal < 0 || floatVal != float64(uint(floatVal)) {
|
||||
return 0, ErrReqParamNotType
|
||||
}
|
||||
return uint(floatVal), nil
|
||||
case int, int8, int16, int32, int64:
|
||||
intVal, ok := cast.(int)
|
||||
if !ok || intVal != int(uint(intVal)) {
|
||||
return 0, ErrReqParamNotType
|
||||
}
|
||||
return uint(intVal), nil
|
||||
case uint, uint8, uint16, uint32, uint64:
|
||||
uintVal, ok := cast.(uint)
|
||||
if !ok {
|
||||
return 0, ErrReqParamNotType
|
||||
}
|
||||
return uintVal, nil
|
||||
default:
|
||||
return 0, ErrReqParamNotType
|
||||
}
|
||||
}
|
||||
|
||||
// GetStrings returns an []slice and an error if not found or invalid type
|
||||
func (rp RequestParam) GetStrings(key string) ([]string, error) {
|
||||
rawValue, err := rp.Get(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch cast := rawValue.(type) {
|
||||
case []fmt.Stringer:
|
||||
strings := make([]string, len(cast))
|
||||
for i, stringer := range cast {
|
||||
strings[i] = stringer.String()
|
||||
}
|
||||
return strings, nil
|
||||
case [][]byte:
|
||||
strings := make([]string, len(cast))
|
||||
for i, bytes := range cast {
|
||||
strings[i] = string(bytes)
|
||||
}
|
||||
return strings, nil
|
||||
case []string:
|
||||
return cast, nil
|
||||
default:
|
||||
return nil, ErrReqParamNotType
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ func NewAny() *Any {
|
|||
}
|
||||
|
||||
// Checker returns the checker function
|
||||
func (Any) Checker(typeName string) typecheck.Checker {
|
||||
func (Any) Checker(typeName string) typecheck.CheckerFunc {
|
||||
// nothing if type not handled
|
||||
if typeName != "any" {
|
||||
return nil
|
||||
|
|
|
@ -11,7 +11,7 @@ func NewBool() *Bool {
|
|||
}
|
||||
|
||||
// Checker returns the checker function
|
||||
func (Bool) Checker(typeName string) typecheck.Checker {
|
||||
func (Bool) Checker(typeName string) typecheck.CheckerFunc {
|
||||
// nothing if type not handled
|
||||
if typeName != "bool" {
|
||||
return nil
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"git.xdrm.io/go/aicra/typecheck"
|
||||
|
@ -16,30 +15,38 @@ func NewFloat64() *Float64 {
|
|||
}
|
||||
|
||||
// Checker returns the checker function
|
||||
func (Float64) Checker(typeName string) typecheck.Checker {
|
||||
func (Float64) Checker(typeName string) typecheck.CheckerFunc {
|
||||
// nothing if type not handled
|
||||
if typeName != "float64" && typeName != "float" {
|
||||
return nil
|
||||
}
|
||||
return func(value interface{}) bool {
|
||||
strVal, isString := value.(string)
|
||||
_, isFloat64 := value.(float64)
|
||||
|
||||
log.Printf("1")
|
||||
|
||||
// raw float
|
||||
if isFloat64 {
|
||||
return true
|
||||
}
|
||||
|
||||
// string float
|
||||
if !isString {
|
||||
return false
|
||||
}
|
||||
_, err := strconv.ParseFloat(strVal, 64)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
_, isFloat := readFloat(value)
|
||||
return isFloat
|
||||
}
|
||||
}
|
||||
|
||||
// readFloat tries to read a serialized float and returns whether it succeeded.
|
||||
func readFloat(value interface{}) (float64, bool) {
|
||||
switch cast := value.(type) {
|
||||
|
||||
case int:
|
||||
return float64(cast), true
|
||||
|
||||
case uint:
|
||||
return float64(cast), true
|
||||
|
||||
case float64:
|
||||
return cast, true
|
||||
|
||||
// serialized string -> try to convert to float
|
||||
case string:
|
||||
floatVal, err := strconv.ParseFloat(cast, 64)
|
||||
return floatVal, err == nil
|
||||
|
||||
// unknown type
|
||||
default:
|
||||
return 0, false
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"git.xdrm.io/go/aicra/typecheck"
|
||||
)
|
||||
|
||||
// Int checks if a value is an int
|
||||
type Int struct{}
|
||||
|
||||
// NewInt returns a bare number type checker
|
||||
func NewInt() *Int {
|
||||
return &Int{}
|
||||
}
|
||||
|
||||
// Checker returns the checker function
|
||||
func (Int) Checker(typeName string) typecheck.CheckerFunc {
|
||||
// nothing if type not handled
|
||||
if typeName != "int" {
|
||||
return nil
|
||||
}
|
||||
return func(value interface{}) bool {
|
||||
cast, isFloat := readFloat(value)
|
||||
|
||||
if !isFloat {
|
||||
return false
|
||||
}
|
||||
|
||||
return cast == float64(int(cast))
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ func NewString() *String {
|
|||
}
|
||||
|
||||
// Checker returns the checker function. Availables type names are : `string`, `string(length)` and `string(minLength, maxLength)`.
|
||||
func (s String) Checker(typeName string) typecheck.Checker {
|
||||
func (s String) Checker(typeName string) typecheck.CheckerFunc {
|
||||
isSimpleString := typeName == "string"
|
||||
fixedLengthMatches := fixedLengthRegex.FindStringSubmatch(typeName)
|
||||
variableLengthMatches := variableLengthRegex.FindStringSubmatch(typeName)
|
||||
|
@ -38,6 +38,7 @@ func (s String) Checker(typeName string) typecheck.Checker {
|
|||
|
||||
// check fixed length
|
||||
if fixedLengthMatches != nil {
|
||||
|
||||
// incoherence fail
|
||||
if len(fixedLengthMatches) < 2 {
|
||||
return false
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
package builtin
|
||||
|
||||
import (
|
||||
"git.xdrm.io/go/aicra/typecheck"
|
||||
)
|
||||
|
||||
// Uint checks if a value is an uint
|
||||
type Uint struct{}
|
||||
|
||||
// NewUint returns a bare number type checker
|
||||
func NewUint() *Uint {
|
||||
return &Uint{}
|
||||
}
|
||||
|
||||
// Checker returns the checker function
|
||||
func (Uint) Checker(typeName string) typecheck.CheckerFunc {
|
||||
// nothing if type not handled
|
||||
if typeName != "uint" {
|
||||
return nil
|
||||
}
|
||||
return func(value interface{}) bool {
|
||||
cast, isFloat := readFloat(value)
|
||||
|
||||
if !isFloat {
|
||||
return false
|
||||
}
|
||||
|
||||
return cast >= 0 && cast == float64(int(cast))
|
||||
}
|
||||
}
|
|
@ -8,11 +8,11 @@ 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
|
||||
// CheckerFunc returns whether a given value fulfills a type
|
||||
type CheckerFunc 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
|
||||
Checker(string) CheckerFunc
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue