gin/binding/query.go

154 lines
3.2 KiB
Go

// Copyright 2017 Manu Martinez-Almeida. All rights reserved.
// Use of this source code is governed by a MIT style
// license that can be found in the LICENSE file.
package binding
import (
"errors"
"fmt"
"net/http"
"reflect"
"time"
)
type queryBinding struct{}
func (queryBinding) Name() string {
return "query"
}
func (queryBinding) Bind(req *http.Request, obj any) error {
values := req.URL.Query()
if err := mapForm(obj, values); err != nil {
return err
}
return validate(obj)
}
var intBitSize = map[reflect.Kind]int{
reflect.Int: 0,
reflect.Int8: 8,
reflect.Int16: 16,
reflect.Int32: 32,
reflect.Int64: 64,
}
var uintBitSize = map[reflect.Kind]int{
reflect.Uint: 0,
reflect.Uint8: 8,
reflect.Uint16: 16,
reflect.Uint32: 32,
reflect.Uint64: 64,
}
var floatBitSize = map[reflect.Kind]int{
reflect.Float32: 32,
reflect.Float64: 64,
}
var durationType = reflect.TypeOf(time.Duration(0))
var timeType = reflect.TypeOf(time.Time{})
func setTime(value string, val reflect.Value) (err error) {
var t time.Time
if t, err = time.ParseInLocation(time.RFC3339, value, time.Local); err != nil {
return err
}
val.Set(reflect.ValueOf(t))
return
}
func parseBaseTypeVar(value string, ptr reflect.Value) (err error) {
val := ptr.Elem()
switch val.Kind() {
// bool
case reflect.Bool:
return setBoolField(value, val)
// string
case reflect.String:
val.SetString(value)
return
case reflect.Int64:
if val.Type() == durationType {
return setTimeDuration(value, val, reflect.StructField{})
}
case reflect.Struct:
if val.Type() == timeType {
return setTime(value, val)
}
}
// int, int8, int16, int32, int64
if bs, ok := intBitSize[val.Kind()]; ok {
return setIntField(value, bs, val)
}
// uint, uint8, uint16, uint32, uint64
if bs, ok := uintBitSize[val.Kind()]; ok {
return setUintField(value, bs, val)
}
// float32 float64
if bs, ok := floatBitSize[val.Kind()]; ok {
return setFloatField(value, bs, val)
}
return nil
}
func setSlice2(values []string, ptr reflect.Value) error {
slice := reflect.MakeSlice(ptr.Elem().Type(), len(values), len(values))
ptr.Elem().Set(slice)
if err := setArray2(values, ptr); err != nil {
return err
}
ptr.Elem().Set(slice)
return nil
}
func setArray2(values []string, ptr reflect.Value) error {
if ptr.Elem().Len() != len(values) {
return fmt.Errorf("Unequal length:%d:%d", ptr.Elem().Len(), len(values))
}
for i, v := range values {
if err := parseBaseTypeVar(v, ptr.Elem().Index(i).Addr()); err != nil {
return err
}
}
return nil
}
// slice, array
// base type
func parseTypeVar(ptr reflect.Value, values []string) error {
switch ptr.Elem().Kind() {
case reflect.Slice:
return setSlice2(values, ptr)
case reflect.Array:
return setArray2(values, ptr)
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map,
reflect.Ptr, reflect.Struct, reflect.UnsafePointer:
if ptr.Elem().Type() == timeType {
return parseBaseTypeVar(values[0], ptr)
}
return errors.New("Unsupported type")
default:
return parseBaseTypeVar(values[0], ptr)
}
}
func SetValue(ptr, defaultVal reflect.Value, values []string, ok bool) error {
if ok {
return parseTypeVar(ptr, values)
}
ptr.Elem().Set(defaultVal)
return nil
}