|
@@ -5,20 +5,19 @@
|
|
|
package binding
|
|
|
|
|
|
import (
|
|
|
+ "encoding/json"
|
|
|
"errors"
|
|
|
- "net/url"
|
|
|
+ "fmt"
|
|
|
"reflect"
|
|
|
"strconv"
|
|
|
"strings"
|
|
|
"time"
|
|
|
+
|
|
|
)
|
|
|
|
|
|
-
|
|
|
-func MapForm(ptr interface{}, values url.Values) error {
|
|
|
- return mapFormByTag(ptr, values, "form")
|
|
|
-}
|
|
|
+var errUnknownType = errors.New("Unknown type")
|
|
|
|
|
|
-func mapURI(ptr interface{}, m map[string][]string) error {
|
|
|
+func mapUri(ptr interface{}, m map[string][]string) error {
|
|
|
return mapFormByTag(ptr, m, "uri")
|
|
|
}
|
|
|
|
|
@@ -26,121 +25,192 @@ func mapForm(ptr interface{}, form map[string][]string) error {
|
|
|
return mapFormByTag(ptr, form, "form")
|
|
|
}
|
|
|
|
|
|
+var emptyField = reflect.StructField{}
|
|
|
+
|
|
|
func mapFormByTag(ptr interface{}, form map[string][]string, tag string) error {
|
|
|
- typ := reflect.TypeOf(ptr).Elem()
|
|
|
- val := reflect.ValueOf(ptr).Elem()
|
|
|
- for i := 0; i < typ.NumField(); i++ {
|
|
|
- typeField := typ.Field(i)
|
|
|
- structField := val.Field(i)
|
|
|
- if !structField.CanSet() {
|
|
|
- continue
|
|
|
- }
|
|
|
+ return mappingByPtr(ptr, formSource(form), tag)
|
|
|
+}
|
|
|
|
|
|
- structFieldKind := structField.Kind()
|
|
|
- inputFieldName := typeField.Tag.Get(tag)
|
|
|
- inputFieldNameList := strings.Split(inputFieldName, ",")
|
|
|
- inputFieldName = inputFieldNameList[0]
|
|
|
- var defaultValue string
|
|
|
- if len(inputFieldNameList) > 1 {
|
|
|
- defaultList := strings.SplitN(inputFieldNameList[1], "=", 2)
|
|
|
- if defaultList[0] == "default" {
|
|
|
- defaultValue = defaultList[1]
|
|
|
- }
|
|
|
+
|
|
|
+type setter interface {
|
|
|
+ TrySet(value reflect.Value, field reflect.StructField, key string, opt setOptions) (isSetted bool, err error)
|
|
|
+}
|
|
|
+
|
|
|
+type formSource map[string][]string
|
|
|
+
|
|
|
+var _ setter = formSource(nil)
|
|
|
+
|
|
|
+
|
|
|
+func (form formSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) {
|
|
|
+ return setByForm(value, field, form, tagValue, opt)
|
|
|
+}
|
|
|
+
|
|
|
+func mappingByPtr(ptr interface{}, setter setter, tag string) error {
|
|
|
+ _, err := mapping(reflect.ValueOf(ptr), emptyField, setter, tag)
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
+func mapping(value reflect.Value, field reflect.StructField, setter setter, tag string) (bool, error) {
|
|
|
+ var vKind = value.Kind()
|
|
|
+
|
|
|
+ if vKind == reflect.Ptr {
|
|
|
+ var isNew bool
|
|
|
+ vPtr := value
|
|
|
+ if value.IsNil() {
|
|
|
+ isNew = true
|
|
|
+ vPtr = reflect.New(value.Type().Elem())
|
|
|
}
|
|
|
- if inputFieldName == "" {
|
|
|
- inputFieldName = typeField.Name
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- if structFieldKind == reflect.Ptr {
|
|
|
- if !structField.Elem().IsValid() {
|
|
|
- structField.Set(reflect.New(structField.Type().Elem()))
|
|
|
- }
|
|
|
- structField = structField.Elem()
|
|
|
- structFieldKind = structField.Kind()
|
|
|
- }
|
|
|
- if structFieldKind == reflect.Struct {
|
|
|
- err := mapFormByTag(structField.Addr().Interface(), form, tag)
|
|
|
- if err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- continue
|
|
|
- }
|
|
|
+ isSetted, err := mapping(vPtr.Elem(), field, setter, tag)
|
|
|
+ if err != nil {
|
|
|
+ return false, err
|
|
|
}
|
|
|
- inputValue, exists := form[inputFieldName]
|
|
|
+ if isNew && isSetted {
|
|
|
+ value.Set(vPtr)
|
|
|
+ }
|
|
|
+ return isSetted, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ 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()
|
|
|
|
|
|
- if !exists {
|
|
|
- if defaultValue == "" {
|
|
|
+ var isSetted bool
|
|
|
+ for i := 0; i < value.NumField(); i++ {
|
|
|
+ if !value.Field(i).CanSet() {
|
|
|
continue
|
|
|
}
|
|
|
- inputValue = make([]string, 1)
|
|
|
- inputValue[0] = defaultValue
|
|
|
+ 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
|
|
|
+}
|
|
|
|
|
|
- numElems := len(inputValue)
|
|
|
- if structFieldKind == reflect.Slice && numElems > 0 {
|
|
|
- sliceOf := structField.Type().Elem().Kind()
|
|
|
- slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
|
|
|
- for i := 0; i < numElems; i++ {
|
|
|
- if err := setWithProperType(sliceOf, inputValue[i], slice.Index(i)); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- }
|
|
|
- val.Field(i).Set(slice)
|
|
|
- continue
|
|
|
+type setOptions struct {
|
|
|
+ isDefaultExists bool
|
|
|
+ defaultValue string
|
|
|
+}
|
|
|
+
|
|
|
+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 == "-" {
|
|
|
+ return false, nil
|
|
|
+ }
|
|
|
+ if tagValue == "" {
|
|
|
+ tagValue = field.Name
|
|
|
+ }
|
|
|
+ if tagValue == "" {
|
|
|
+ return false, nil
|
|
|
+ }
|
|
|
+
|
|
|
+ var opt string
|
|
|
+ for len(opts) > 0 {
|
|
|
+ opt, opts = head(opts, ",")
|
|
|
+
|
|
|
+ k, v := head(opt, "=")
|
|
|
+ switch k {
|
|
|
+ case "default":
|
|
|
+ setOpt.isDefaultExists = true
|
|
|
+ setOpt.defaultValue = v
|
|
|
}
|
|
|
- if _, isTime := structField.Interface().(time.Time); isTime {
|
|
|
- if err := setTimeField(inputValue[0], typeField, structField); err != nil {
|
|
|
- return err
|
|
|
- }
|
|
|
- continue
|
|
|
+ }
|
|
|
+
|
|
|
+ return setter.TrySet(value, field, tagValue, setOpt)
|
|
|
+}
|
|
|
+
|
|
|
+func setByForm(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 err := setWithProperType(typeField.Type.Kind(), inputValue[0], structField); err != nil {
|
|
|
- return err
|
|
|
+ return true, setSlice(vs, value, field)
|
|
|
+ case reflect.Array:
|
|
|
+ if !ok {
|
|
|
+ vs = []string{opt.defaultValue}
|
|
|
+ }
|
|
|
+ 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, setWithProperType(val, value, field)
|
|
|
}
|
|
|
- return nil
|
|
|
}
|
|
|
|
|
|
-func setWithProperType(valueKind reflect.Kind, val string, structField reflect.Value) error {
|
|
|
- switch valueKind {
|
|
|
+func setWithProperType(val string, value reflect.Value, field reflect.StructField) error {
|
|
|
+ switch value.Kind() {
|
|
|
case reflect.Int:
|
|
|
- return setIntField(val, 0, structField)
|
|
|
+ return setIntField(val, 0, value)
|
|
|
case reflect.Int8:
|
|
|
- return setIntField(val, 8, structField)
|
|
|
+ return setIntField(val, 8, value)
|
|
|
case reflect.Int16:
|
|
|
- return setIntField(val, 16, structField)
|
|
|
+ return setIntField(val, 16, value)
|
|
|
case reflect.Int32:
|
|
|
- return setIntField(val, 32, structField)
|
|
|
+ return setIntField(val, 32, value)
|
|
|
case reflect.Int64:
|
|
|
- return setIntField(val, 64, structField)
|
|
|
+ switch value.Interface().(type) {
|
|
|
+ case time.Duration:
|
|
|
+ return setTimeDuration(val, value, field)
|
|
|
+ }
|
|
|
+ return setIntField(val, 64, value)
|
|
|
case reflect.Uint:
|
|
|
- return setUintField(val, 0, structField)
|
|
|
+ return setUintField(val, 0, value)
|
|
|
case reflect.Uint8:
|
|
|
- return setUintField(val, 8, structField)
|
|
|
+ return setUintField(val, 8, value)
|
|
|
case reflect.Uint16:
|
|
|
- return setUintField(val, 16, structField)
|
|
|
+ return setUintField(val, 16, value)
|
|
|
case reflect.Uint32:
|
|
|
- return setUintField(val, 32, structField)
|
|
|
+ return setUintField(val, 32, value)
|
|
|
case reflect.Uint64:
|
|
|
- return setUintField(val, 64, structField)
|
|
|
+ return setUintField(val, 64, value)
|
|
|
case reflect.Bool:
|
|
|
- return setBoolField(val, structField)
|
|
|
+ return setBoolField(val, value)
|
|
|
case reflect.Float32:
|
|
|
- return setFloatField(val, 32, structField)
|
|
|
+ return setFloatField(val, 32, value)
|
|
|
case reflect.Float64:
|
|
|
- return setFloatField(val, 64, structField)
|
|
|
+ return setFloatField(val, 64, value)
|
|
|
case reflect.String:
|
|
|
- structField.SetString(val)
|
|
|
- case reflect.Ptr:
|
|
|
- if !structField.Elem().IsValid() {
|
|
|
- structField.Set(reflect.New(structField.Type().Elem()))
|
|
|
+ value.SetString(val)
|
|
|
+ case reflect.Struct:
|
|
|
+ switch value.Interface().(type) {
|
|
|
+ case time.Time:
|
|
|
+ return setTimeField(val, field, value)
|
|
|
}
|
|
|
- structFieldElem := structField.Elem()
|
|
|
- return setWithProperType(structFieldElem.Kind(), val, structFieldElem)
|
|
|
+ return json.Unmarshal([]byte(val), value.Addr().Interface())
|
|
|
+ case reflect.Map:
|
|
|
+ return json.Unmarshal([]byte(val), value.Addr().Interface())
|
|
|
default:
|
|
|
- return errors.New("Unknown type")
|
|
|
+ return errUnknownType
|
|
|
}
|
|
|
return nil
|
|
|
}
|
|
@@ -221,3 +291,40 @@ func setTimeField(val string, structField reflect.StructField, value reflect.Val
|
|
|
value.Set(reflect.ValueOf(t))
|
|
|
return nil
|
|
|
}
|
|
|
+
|
|
|
+func setArray(vals []string, value reflect.Value, field reflect.StructField) error {
|
|
|
+ for i, s := range vals {
|
|
|
+ err := setWithProperType(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 setTimeDuration(val string, value reflect.Value, field reflect.StructField) error {
|
|
|
+ d, err := time.ParseDuration(val)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ value.Set(reflect.ValueOf(d))
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+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):]
|
|
|
+}
|