forked from Fuyu/forms
Compare commits
4 Commits
Author | SHA1 | Date |
---|---|---|
Nise Void | 1b29fa463b | |
Nise Void | 34a9eba03a | |
Nise Void | 0efb52d29e | |
Elwin Tamminga | 056b574ce1 |
80
forms.go
80
forms.go
|
@ -8,18 +8,28 @@ import (
|
|||
"unicode"
|
||||
)
|
||||
|
||||
type DecodeError struct {
|
||||
Field string
|
||||
Type string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (de DecodeError) Error() string {
|
||||
return `Failed to decode "` + de.Value + `" into field ` + de.Field + ` of type ` + de.Type + `.`
|
||||
}
|
||||
|
||||
type Values = map[string][]string
|
||||
|
||||
func Decode(form Values, v interface{}) {
|
||||
func Decode(form Values, v interface{}) error {
|
||||
rv := reflect.ValueOf(v)
|
||||
if rv.Kind() != reflect.Ptr {
|
||||
panic(`Input is not a pointer`)
|
||||
}
|
||||
|
||||
decode(form, rv.Elem(), ``)
|
||||
return decode(form, rv.Elem(), ``)
|
||||
}
|
||||
|
||||
func decode(form Values, rv reflect.Value, prefix string) {
|
||||
func decode(form Values, rv reflect.Value, prefix string) error {
|
||||
for i := 0; i < rv.NumField(); i++ {
|
||||
ft, fv := rv.Type().Field(i), rv.Field(i)
|
||||
|
||||
|
@ -60,11 +70,15 @@ func decode(form Values, rv reflect.Value, prefix string) {
|
|||
fv = fv.Elem()
|
||||
}
|
||||
|
||||
decode(form, fv, getPrefix(ft, prefix))
|
||||
err := decode(form, fv, getPrefix(ft, prefix))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
v, ok := form[getName(ft, prefix)]
|
||||
fieldName := getName(ft, prefix)
|
||||
v, ok := form[fieldName]
|
||||
if !ok || len(v) == 0 {
|
||||
continue
|
||||
}
|
||||
|
@ -75,23 +89,55 @@ func decode(form Values, rv reflect.Value, prefix string) {
|
|||
fv.Set(reflect.New(ft.Type.Elem()))
|
||||
}
|
||||
|
||||
setValue(v, fv)
|
||||
err := setValue(fieldName, v, fv)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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 setValue(fieldName string, values []string, fv reflect.Value) error {
|
||||
if fv.Elem().Type().Kind() != reflect.Slice {
|
||||
return parse(fieldName, values[0], fv)
|
||||
}
|
||||
|
||||
slice := reflect.MakeSlice(fv.Elem().Type(), len(values), len(values))
|
||||
val := reflect.New(slice.Type().Elem())
|
||||
for i, v := range values {
|
||||
err := parse(fieldName, v, val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
slice.Index(i).Set(val.Elem())
|
||||
}
|
||||
fv.Elem().Set(slice)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func parse(fieldName, v string, fv reflect.Value) error {
|
||||
var err error
|
||||
|
||||
switch f := fv.Interface().(type) {
|
||||
case *string:
|
||||
*f = v
|
||||
case *int:
|
||||
*f, err = strconv.Atoi(v)
|
||||
case *float32:
|
||||
var f64 float64
|
||||
f64, err = strconv.ParseFloat(v, 32)
|
||||
*f = float32(f64)
|
||||
case *float64:
|
||||
*f, err = strconv.ParseFloat(v, 64)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return DecodeError{fieldName, fv.Elem().Type().Kind().String(), v}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPrefix(ft reflect.StructField, prefix string) string {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package forms
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -15,6 +15,10 @@ type testStruct struct {
|
|||
|
||||
PA *string
|
||||
PD *nested
|
||||
|
||||
SA []string
|
||||
SB []int
|
||||
SC []float64
|
||||
}
|
||||
|
||||
type nested struct {
|
||||
|
@ -27,26 +31,75 @@ type Anon struct {
|
|||
E string
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
data := &testStruct{}
|
||||
var input = Values{
|
||||
`A`: {`test`},
|
||||
`B`: {`10`},
|
||||
`C`: {`1.25`},
|
||||
|
||||
Decode(Values{
|
||||
`A`: {`test`},
|
||||
`B`: {`10`},
|
||||
`C`: {`1.25`},
|
||||
`D.A`: {`test2`},
|
||||
`D.B`: {`20`},
|
||||
`D.C`: {`2.50`},
|
||||
|
||||
`D.A`: {`test2`},
|
||||
`D.B`: {`20`},
|
||||
`D.C`: {`2.50`},
|
||||
`E`: {`test3`},
|
||||
|
||||
`E`: {`test3`},
|
||||
`PA`: {`test4`},
|
||||
|
||||
`PA`: {`test4`},
|
||||
`PD.A`: {`test5`},
|
||||
`PD.B`: {`30`},
|
||||
`PD.C`: {`3.75`},
|
||||
|
||||
`PD.A`: {`test5`},
|
||||
`PD.B`: {`30`},
|
||||
`PD.C`: {`3.75`},
|
||||
}, data)
|
||||
|
||||
fmt.Println(`data:`, data, *data.PA, *data.PD)
|
||||
`SA`: {`test6`, `slice`},
|
||||
`SB`: {`3`, `2`},
|
||||
`SC`: {`4.50`, `1.32`},
|
||||
}
|
||||
|
||||
var expected = testStruct{
|
||||
A: `test`,
|
||||
B: 10,
|
||||
C: 1.25,
|
||||
|
||||
D: nested{A: `test2`, B: 20, C: 2.50},
|
||||
|
||||
Anon: Anon{E: `test3`},
|
||||
|
||||
SA: []string{`test6`, `slice`},
|
||||
SB: []int{3, 2},
|
||||
SC: []float64{4.50, 1.32},
|
||||
}
|
||||
|
||||
var expectedPA = `test4`
|
||||
var expectedPD = nested{`test5`, 30, 3.75}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
data := testStruct{}
|
||||
|
||||
_ = Decode(input, &data)
|
||||
|
||||
if *data.PA != expectedPA {
|
||||
t.Errorf(`PA was incorrect. Expected %v but got %v`, expectedPA, *data.PA)
|
||||
}
|
||||
if *data.PD != expectedPD {
|
||||
t.Errorf(`PA was incorrect. Expected %v but got %v`, expectedPD, *data.PD)
|
||||
}
|
||||
|
||||
expected.PA, expected.PD = data.PA, data.PD
|
||||
if !reflect.DeepEqual(&data, &expected) {
|
||||
t.Errorf("Data did not match expected output. Expected \n%v\nbut got\n%v", expected, data)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeError(t *testing.T) {
|
||||
var data struct {
|
||||
A struct {
|
||||
B float64
|
||||
}
|
||||
}
|
||||
|
||||
err := Decode(Values{`A.B`: {`a`}}, &data)
|
||||
|
||||
if err == nil {
|
||||
t.Error(`Expected error but got nil`)
|
||||
}
|
||||
|
||||
t.Log(err)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue