Initial commit
This commit is contained in:
commit
e1c984b07c
111
forms.go
Normal file
111
forms.go
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
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(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
|
||||||
|
}
|
52
forms_test.go
Normal file
52
forms_test.go
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
package forms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
type testStruct struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
C float64
|
||||||
|
D nested
|
||||||
|
|
||||||
|
Anon
|
||||||
|
|
||||||
|
PA *string
|
||||||
|
PD *nested
|
||||||
|
}
|
||||||
|
|
||||||
|
type nested struct {
|
||||||
|
A string
|
||||||
|
B int
|
||||||
|
C float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type Anon struct {
|
||||||
|
E string
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDecode(t *testing.T) {
|
||||||
|
data := &testStruct{}
|
||||||
|
|
||||||
|
Decode(Values{
|
||||||
|
`A`: {`test`},
|
||||||
|
`B`: {`10`},
|
||||||
|
`C`: {`1.25`},
|
||||||
|
|
||||||
|
`D.A`: {`test2`},
|
||||||
|
`D.B`: {`20`},
|
||||||
|
`D.C`: {`2.50`},
|
||||||
|
|
||||||
|
`E`: {`test3`},
|
||||||
|
|
||||||
|
`PA`: {`test4`},
|
||||||
|
|
||||||
|
`PD.A`: {`test5`},
|
||||||
|
`PD.B`: {`30`},
|
||||||
|
`PD.C`: {`3.75`},
|
||||||
|
}, data)
|
||||||
|
|
||||||
|
fmt.Println(`data:`, data, *data.PA, *data.PD)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user