126 lines
2.1 KiB
Go
126 lines
2.1 KiB
Go
package forms
|
|
|
|
import (
|
|
"reflect"
|
|
"strconv"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
type Values = map[string][]string
|
|
|
|
func Decode(form Values, v interface{}) {
|
|
rv := reflect.ValueOf(v)
|
|
if rv.Kind() != reflect.Ptr {
|
|
panic(`Input is not a pointer`)
|
|
}
|
|
|
|
decode(form, rv.Elem(), ``)
|
|
}
|
|
|
|
func decode(form Values, rv reflect.Value, prefix string) {
|
|
for i := 0; i < rv.NumField(); i++ {
|
|
ft, fv := rv.Type().Field(i), rv.Field(i)
|
|
|
|
if !unicode.IsUpper(rune(ft.Name[0])) {
|
|
continue
|
|
}
|
|
|
|
// TODO: Add custom interface
|
|
|
|
fe := ft.Type
|
|
if fe.Kind() == reflect.Ptr {
|
|
fe = fe.Elem()
|
|
}
|
|
|
|
if fe.Kind() == reflect.Struct {
|
|
if fv.Kind() == reflect.Ptr {
|
|
found := false
|
|
for k, _ := range form {
|
|
if strings.HasPrefix(k, getPrefix(ft, prefix)) {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
if !found {
|
|
continue
|
|
}
|
|
|
|
fv.Set(reflect.New(ft.Type.Elem()))
|
|
|
|
fv = fv.Elem()
|
|
}
|
|
|
|
decode(form, fv, getPrefix(ft, prefix))
|
|
continue
|
|
}
|
|
|
|
v, ok := form[getName(ft, prefix)]
|
|
if !ok || len(v) == 0 {
|
|
continue
|
|
}
|
|
|
|
if fv.Kind() != reflect.Ptr {
|
|
fv = fv.Addr()
|
|
} else if fv.IsNil() {
|
|
fv.Set(reflect.New(ft.Type.Elem()))
|
|
}
|
|
|
|
setValue(v, fv)
|
|
}
|
|
}
|
|
|
|
func setValue(values []string, fv reflect.Value) {
|
|
if fv.Elem().Type().Kind() != reflect.Slice {
|
|
parse(values[0], fv)
|
|
return
|
|
}
|
|
|
|
slice := reflect.MakeSlice(fv.Elem().Type(), len(values), len(values))
|
|
val := reflect.New(slice.Type().Elem())
|
|
for i, v := range values {
|
|
parse(v, val)
|
|
slice.Index(i).Set(val.Elem())
|
|
}
|
|
fv.Elem().Set(slice)
|
|
}
|
|
|
|
func parse(v string, fv reflect.Value) {
|
|
switch f := fv.Interface().(type) {
|
|
case *string:
|
|
*f = v
|
|
case *int:
|
|
*f, _ = strconv.Atoi(v)
|
|
case *float32:
|
|
v, _ := strconv.ParseFloat(v, 32)
|
|
*f = float32(v)
|
|
case *float64:
|
|
*f, _ = strconv.ParseFloat(v, 64)
|
|
}
|
|
}
|
|
|
|
func getPrefix(ft reflect.StructField, prefix string) string {
|
|
if ft.Anonymous {
|
|
return prefix
|
|
}
|
|
|
|
if prefix != `` {
|
|
prefix += `.`
|
|
}
|
|
|
|
return prefix + ft.Name
|
|
}
|
|
|
|
func getName(ft reflect.StructField, prefix string) string {
|
|
name := ft.Tag.Get(`form`)
|
|
if name == `` {
|
|
name = ft.Name
|
|
}
|
|
|
|
if prefix == `` {
|
|
return name
|
|
}
|
|
|
|
return prefix + `.` + name
|
|
}
|