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
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
// ConstError is a wrapper to set constant errors
|
// ConstError is a wrapper to set constant errors
|
||||||
type ConstError string
|
type ConstError string
|
||||||
|
|
||||||
|
@ -26,17 +30,133 @@ func (rp RequestParam) Get(key string) (interface{}, error) {
|
||||||
return rawValue, nil
|
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) {
|
func (rp RequestParam) GetString(key string) (string, error) {
|
||||||
rawValue, err := rp.Get(key)
|
rawValue, err := rp.Get(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
convertedValue, canConvert := rawValue.(string)
|
switch cast := rawValue.(type) {
|
||||||
if !canConvert {
|
case fmt.Stringer:
|
||||||
|
return cast.String(), nil
|
||||||
|
case []byte:
|
||||||
|
return string(cast), nil
|
||||||
|
case string:
|
||||||
|
return cast, nil
|
||||||
|
default:
|
||||||
return "", ErrReqParamNotType
|
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
|
// Checker returns the checker function
|
||||||
func (Any) Checker(typeName string) typecheck.Checker {
|
func (Any) Checker(typeName string) typecheck.CheckerFunc {
|
||||||
// nothing if type not handled
|
// nothing if type not handled
|
||||||
if typeName != "any" {
|
if typeName != "any" {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -11,7 +11,7 @@ func NewBool() *Bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checker returns the checker function
|
// Checker returns the checker function
|
||||||
func (Bool) Checker(typeName string) typecheck.Checker {
|
func (Bool) Checker(typeName string) typecheck.CheckerFunc {
|
||||||
// nothing if type not handled
|
// nothing if type not handled
|
||||||
if typeName != "bool" {
|
if typeName != "bool" {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package builtin
|
package builtin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"git.xdrm.io/go/aicra/typecheck"
|
"git.xdrm.io/go/aicra/typecheck"
|
||||||
|
@ -16,30 +15,38 @@ func NewFloat64() *Float64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checker returns the checker function
|
// Checker returns the checker function
|
||||||
func (Float64) Checker(typeName string) typecheck.Checker {
|
func (Float64) Checker(typeName string) typecheck.CheckerFunc {
|
||||||
// nothing if type not handled
|
// nothing if type not handled
|
||||||
if typeName != "float64" && typeName != "float" {
|
if typeName != "float64" && typeName != "float" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return func(value interface{}) bool {
|
return func(value interface{}) bool {
|
||||||
strVal, isString := value.(string)
|
_, isFloat := readFloat(value)
|
||||||
_, isFloat64 := value.(float64)
|
return isFloat
|
||||||
|
}
|
||||||
log.Printf("1")
|
|
||||||
|
|
||||||
// raw float
|
|
||||||
if isFloat64 {
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// string float
|
// readFloat tries to read a serialized float and returns whether it succeeded.
|
||||||
if !isString {
|
func readFloat(value interface{}) (float64, bool) {
|
||||||
return false
|
switch cast := value.(type) {
|
||||||
}
|
|
||||||
_, err := strconv.ParseFloat(strVal, 64)
|
case int:
|
||||||
if err != nil {
|
return float64(cast), true
|
||||||
return false
|
|
||||||
}
|
case uint:
|
||||||
return true
|
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)`.
|
// 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"
|
isSimpleString := typeName == "string"
|
||||||
fixedLengthMatches := fixedLengthRegex.FindStringSubmatch(typeName)
|
fixedLengthMatches := fixedLengthRegex.FindStringSubmatch(typeName)
|
||||||
variableLengthMatches := variableLengthRegex.FindStringSubmatch(typeName)
|
variableLengthMatches := variableLengthRegex.FindStringSubmatch(typeName)
|
||||||
|
@ -38,6 +38,7 @@ func (s String) Checker(typeName string) typecheck.Checker {
|
||||||
|
|
||||||
// check fixed length
|
// check fixed length
|
||||||
if fixedLengthMatches != nil {
|
if fixedLengthMatches != nil {
|
||||||
|
|
||||||
// incoherence fail
|
// incoherence fail
|
||||||
if len(fixedLengthMatches) < 2 {
|
if len(fixedLengthMatches) < 2 {
|
||||||
return false
|
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
|
// ErrDoesNotMatch when the value is invalid
|
||||||
var ErrDoesNotMatch = errors.New("does not match")
|
var ErrDoesNotMatch = errors.New("does not match")
|
||||||
|
|
||||||
// Checker returns whether a given value fulfills a type
|
// CheckerFunc returns whether a given value fulfills a type
|
||||||
type Checker func(interface{}) bool
|
type CheckerFunc func(interface{}) bool
|
||||||
|
|
||||||
// Type represents a type checker
|
// Type represents a type checker
|
||||||
type Type interface {
|
type Type interface {
|
||||||
// given a type name, returns the checker function or NIL if the type is not handled here
|
// 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