package forms import ( "encoding" "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 } fe := ft.Type if fe.Kind() == reflect.Ptr { fe = fe.Elem() } if tu, ok := fv.Addr().Interface().(encoding.TextUnmarshaler); ok { v, ok := form[getName(ft, prefix)] if !ok || len(v) == 0 { continue } tu.UnmarshalText([]byte(v[0])) continue } 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(v []string, fv reflect.Value) { switch f := fv.Interface().(type) { case *string: *f = v[0] case *int: *f, _ = strconv.Atoi(v[0]) case *float32: v, _ := strconv.ParseFloat(v[0], 32) *f = float32(v) case *float64: *f, _ = strconv.ParseFloat(v[0], 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 }