2025-04-30 15:07:43 +08:00

358 lines
7.7 KiB
Go

package request
import (
"encoding/json"
"fmt"
"reflect"
"strconv"
"strings"
"time"
)
var (
_EmptyField = reflect.StructField{}
)
type _SetOptions struct {
isDefaultExists bool
defaultValue string
}
type _Setter interface {
TrySet(value reflect.Value, field reflect.StructField, key string, opt _SetOptions) (isSetted bool, err error)
}
func head(str, sep string) (head string, tail string) {
idx := strings.Index(str, sep)
if idx < 0 {
return str, ""
}
return str[:idx], str[idx+len(sep):]
}
func _ValueSet(value reflect.Value, field reflect.StructField, form map[string][]string, tagValue string, opt _SetOptions) (isSetted bool, err error) {
vs, ok := form[tagValue]
if !ok && !opt.isDefaultExists {
return false, nil
}
switch value.Kind() {
case reflect.Slice:
if !ok {
vs = []string{opt.defaultValue}
}
if ok, err = _SliceUnmarshaler(value, vs); ok {
return true, err
}
return true, _SetSlice(vs, value, field)
case reflect.Array:
if !ok {
vs = []string{opt.defaultValue}
}
if ok, err = _SliceUnmarshaler(value, vs); ok {
return true, err
}
if len(vs) != value.Len() {
return false, fmt.Errorf("%q is not valid value for %s", vs, value.Type().String())
}
return true, _SetArray(vs, value, field)
default:
var val string
if !ok {
val = opt.defaultValue
}
if len(vs) > 0 {
val = vs[0]
}
return true, _SetValue(val, value, field)
}
}
func _TryToSetValue(value reflect.Value, field reflect.StructField, setter _Setter, tag string) (bool, error) {
var tagValue string
var setOpt _SetOptions
tagValue = field.Tag.Get(tag)
tagValue, opts := head(tagValue, ",")
if tagValue == "" { // default value is FieldName
tagValue = field.Name
}
if tagValue == "" { // when field is "_EmptyField" variable
return false, nil
}
var opt string
for len(opts) > 0 {
opt, opts = head(opts, ",")
if k, v := head(opt, "="); k == "default" {
setOpt.isDefaultExists = true
setOpt.defaultValue = v
}
}
return setter.TrySet(value, field, tagValue, setOpt)
}
func _Mapping(value reflect.Value, field reflect.StructField, setter _Setter, tag string) (bool, error) {
if field.Tag.Get(tag) == "-" { // just ignoring this field
return false, nil
}
var vKind = value.Kind()
if vKind == reflect.Ptr {
var isNew bool
vPtr := value
if value.IsNil() {
isNew = true
vPtr = reflect.New(value.Type().Elem())
}
isSetted, err := _Mapping(vPtr.Elem(), field, setter, tag)
if err != nil {
return false, err
}
if isNew && isSetted {
value.Set(vPtr)
}
return isSetted, nil
}
if vKind != reflect.Struct || !field.Anonymous {
ok, err := _TryToSetValue(value, field, setter, tag)
if err != nil {
return false, err
}
if ok {
return true, nil
}
}
if vKind == reflect.Struct {
tValue := value.Type()
var isSetted bool
for i := 0; i < value.NumField(); i++ {
sf := tValue.Field(i)
if sf.PkgPath != "" && !sf.Anonymous { // unexported
continue
}
ok, err := _Mapping(value.Field(i), tValue.Field(i), setter, tag)
if err != nil {
return false, err
}
isSetted = isSetted || ok
}
return isSetted, nil
}
return false, nil
}
func _SetIntField(val string, bitSize int, value reflect.Value) error {
if val == "" {
val = "0"
}
intVal, err := strconv.ParseInt(val, 10, bitSize)
if err == nil {
value.SetInt(intVal)
}
return err
}
func _SetUintField(val string, bitSize int, value reflect.Value) error {
if val == "" {
val = "0"
}
uintVal, err := strconv.ParseUint(val, 10, bitSize)
if err == nil {
value.SetUint(uintVal)
}
return err
}
func _SetBoolField(val string, value reflect.Value) error {
if val == "" {
val = "false"
}
boolVal, err := strconv.ParseBool(val)
if err == nil {
value.SetBool(boolVal)
}
return err
}
func _SetFloatField(val string, bitSize int, value reflect.Value) error {
if val == "" {
val = "0.0"
}
floatVal, err := strconv.ParseFloat(val, bitSize)
if err == nil {
value.SetFloat(floatVal)
}
return err
}
func _SetTimeField(val string, structField reflect.StructField, value reflect.Value) error {
timeFormat := structField.Tag.Get("time_format")
if timeFormat == "" {
timeFormat = time.RFC3339
}
switch tf := strings.ToLower(timeFormat); tf {
case "unix", "unixnano":
tv, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return err
}
d := time.Duration(1)
if tf == "unixnano" {
d = time.Second
}
t := time.Unix(tv/int64(d), tv%int64(d))
value.Set(reflect.ValueOf(t))
return nil
}
if val == "" {
value.Set(reflect.ValueOf(time.Time{}))
return nil
}
l := time.Local
if isUTC, _ := strconv.ParseBool(structField.Tag.Get("time_utc")); isUTC {
l = time.UTC
}
if locTag := structField.Tag.Get("time_location"); locTag != "" {
loc, err := time.LoadLocation(locTag)
if err != nil {
return err
}
l = loc
}
t, err := time.ParseInLocation(timeFormat, val, l)
if err != nil {
return err
}
value.Set(reflect.ValueOf(t))
return nil
}
func _SetTimeDuration(val string, value reflect.Value) error {
d, err := time.ParseDuration(val)
if err != nil {
return err
}
value.Set(reflect.ValueOf(d))
return nil
}
func _SetArray(vals []string, value reflect.Value, field reflect.StructField) error {
for i, s := range vals {
err := _SetValue(s, value.Index(i), field)
if err != nil {
return err
}
}
return nil
}
func _SetSlice(vals []string, value reflect.Value, field reflect.StructField) error {
slice := reflect.MakeSlice(value.Type(), len(vals), len(vals))
err := _SetArray(vals, slice, field)
if err != nil {
return err
}
value.Set(slice)
return nil
}
func _SliceUnmarshaler(v reflect.Value, vs []string) (bool, error) {
if v.Kind() != reflect.Pointer && v.Type().Name() != "" && v.CanAddr() {
v = v.Addr()
}
if v.Type().NumMethod() > 0 && v.CanInterface() {
if u, ok := v.Interface().(SliceUnmarshaler); ok {
return true, u.UnmarshalForm(vs)
}
if u, ok := v.Interface().(Unmarshaler); ok {
var val string
if len(vs) > 0 {
val = vs[0]
}
return true, u.UnmarshalForm(val)
}
}
return false, nil
}
func _ValueUnmarshaler(v reflect.Value) Unmarshaler {
if v.Kind() != reflect.Pointer && v.Type().Name() != "" && v.CanAddr() {
v = v.Addr()
}
if v.Type().NumMethod() > 0 && v.CanInterface() {
if u, ok := v.Interface().(Unmarshaler); ok {
return u
}
}
return nil
}
func _SetValue(val string, value reflect.Value, field reflect.StructField) error {
if u := _ValueUnmarshaler(value); u != nil {
return u.UnmarshalForm(val)
}
switch value.Kind() {
case reflect.Int:
return _SetIntField(val, 0, value)
case reflect.Int8:
return _SetIntField(val, 8, value)
case reflect.Int16:
return _SetIntField(val, 16, value)
case reflect.Int32:
return _SetIntField(val, 32, value)
case reflect.Int64:
switch value.Interface().(type) {
case time.Duration:
return _SetTimeDuration(val, value)
}
return _SetIntField(val, 64, value)
case reflect.Uint:
return _SetUintField(val, 0, value)
case reflect.Uint8:
return _SetUintField(val, 8, value)
case reflect.Uint16:
return _SetUintField(val, 16, value)
case reflect.Uint32:
return _SetUintField(val, 32, value)
case reflect.Uint64:
return _SetUintField(val, 64, value)
case reflect.Bool:
return _SetBoolField(val, value)
case reflect.Float32:
return _SetFloatField(val, 32, value)
case reflect.Float64:
return _SetFloatField(val, 64, value)
case reflect.String:
value.SetString(val)
case reflect.Struct:
switch value.Interface().(type) {
case time.Time:
return _SetTimeField(val, field, value)
}
return json.Unmarshal(StringToBytes(val), value.Addr().Interface())
case reflect.Map:
return json.Unmarshal(StringToBytes(val), value.Addr().Interface())
default:
return ErrUnknownType
}
return nil
}