forked from Fuyu/forms
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