Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactors form mapping into smaller functions for clarity #41

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
62 changes: 0 additions & 62 deletions binding.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package binding
import (
"encoding/json"
"io"
"mime/multipart"
"net/http"
"reflect"
"strconv"
Expand Down Expand Up @@ -229,67 +228,6 @@ func validateStruct(errors Errors, obj interface{}) Errors {
return errors
}

// Takes values from the form data and puts them into a struct
func mapForm(formStruct reflect.Value, form map[string][]string,
formfile map[string][]*multipart.FileHeader, errors Errors) {

if formStruct.Kind() == reflect.Ptr {
formStruct = formStruct.Elem()
}
typ := formStruct.Type()

for i := 0; i < typ.NumField(); i++ {
typeField := typ.Field(i)
structField := formStruct.Field(i)

if typeField.Type.Kind() == reflect.Ptr && typeField.Anonymous {
structField.Set(reflect.New(typeField.Type.Elem()))
mapForm(structField.Elem(), form, formfile, errors)
if reflect.DeepEqual(structField.Elem().Interface(), reflect.Zero(structField.Elem().Type()).Interface()) {
structField.Set(reflect.Zero(structField.Type()))
}
} else if typeField.Type.Kind() == reflect.Struct {
mapForm(structField, form, formfile, errors)
} else if inputFieldName := typeField.Tag.Get("form"); inputFieldName != "" {
if !structField.CanSet() {
continue
}

inputValue, exists := form[inputFieldName]
if exists {
numElems := len(inputValue)
if structField.Kind() == reflect.Slice && numElems > 0 {
sliceOf := structField.Type().Elem().Kind()
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for i := 0; i < numElems; i++ {
setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors)
}
formStruct.Field(i).Set(slice)
} else {
setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors)
}
continue
}

inputFile, exists := formfile[inputFieldName]
if !exists {
continue
}
fhType := reflect.TypeOf((*multipart.FileHeader)(nil))
numElems := len(inputFile)
if structField.Kind() == reflect.Slice && numElems > 0 && structField.Type().Elem() == fhType {
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for i := 0; i < numElems; i++ {
slice.Index(i).Set(reflect.ValueOf(inputFile[i]))
}
structField.Set(slice)
} else if structField.Type() == fhType {
structField.Set(reflect.ValueOf(inputFile[0]))
}
}
}
}

// ErrorHandler simply counts the number of errors in the
// context and, if more than 0, writes a response with an
// error code and a JSON payload describing the errors.
Expand Down
130 changes: 130 additions & 0 deletions form_mapping.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package binding

import(
"mime/multipart"
"reflect"
)

// Takes values from the form data and puts them into a struct
func mapForm(formStruct reflect.Value, form map[string][]string,
formfile map[string][]*multipart.FileHeader, errors Errors) {

if formStruct.Kind() == reflect.Ptr {
formStruct = formStruct.Elem()
}

for i := 0; i < formStruct.Type().NumField(); i++ {
typeField := formStruct.Type().Field(i)
structField := formStruct.Field(i)
mapFormField(typeField, structField, form, formfile, errors)
}
}

func mapFormField(typeField reflect.StructField,
structField reflect.Value, form map[string][]string,
formfile map[string][]*multipart.FileHeader, errors Errors) {

if !structField.CanSet() {
return
}

if typeField.Type.Kind() == reflect.Ptr && typeField.Anonymous {
structField.Set(reflect.New(typeField.Type.Elem()))
mapForm(structField.Elem(), form, formfile, errors)
if reflect.DeepEqual(structField.Elem().Interface(), reflect.Zero(structField.Elem().Type()).Interface()) {
structField.Set(reflect.Zero(structField.Type()))
}
} else if typeField.Type.Kind() == reflect.Struct {
mapForm(structField, form, formfile, errors)
} else if typeField.Type.Kind() == reflect.Slice {
mapFormFieldSlice(typeField, structField, form, formfile, errors)
} else {
mapFormFieldValue(typeField, structField, form, formfile, errors)
}
}

func mapFormFieldSlice(typeField reflect.StructField,
structField reflect.Value, form map[string][]string,
formfile map[string][]*multipart.FileHeader, errors Errors) {

if reflect.TypeOf((*multipart.FileHeader)(nil)) == structField.Type().Elem() {
mapFormFieldMultipart(typeField, structField, formfile, errors)
} else if structField.Type().Elem().Kind() == reflect.Struct {
// TODO: element in slice is struct, iterate all and call mapForm
} else {
mapFormFieldSliceBuiltin(typeField, structField, form, errors)
}
}

func mapFormFieldSliceBuiltin(typeField reflect.StructField,
structField reflect.Value, form map[string][]string, errors Errors) {

inputFieldName := typeField.Tag.Get("form")
if inputFieldName == "" {
return
}

inputValue, exists := form[inputFieldName]
if !exists {
return
}

numElems := len(inputValue)
if numElems > 0 {
sliceOf := structField.Type().Elem().Kind()
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for i := 0; i < numElems; i++ {
setWithProperType(sliceOf, inputValue[i], slice.Index(i), inputFieldName, errors)
}
structField.Set(slice)
}
}

func mapFormFieldValue(typeField reflect.StructField,
structField reflect.Value, form map[string][]string,
formfile map[string][]*multipart.FileHeader, errors Errors) {

// handle multipart separately
if structField.Type() == reflect.TypeOf((*multipart.FileHeader)(nil)) {
mapFormFieldMultipart(typeField, structField, formfile, errors)
return
}

inputFieldName := typeField.Tag.Get("form")
if inputFieldName == "" {
return
}

inputValue, exists := form[inputFieldName]
if !exists {
return
}

setWithProperType(typeField.Type.Kind(), inputValue[0], structField, inputFieldName, errors)
}

func mapFormFieldMultipart(typeField reflect.StructField,
structField reflect.Value,
formfile map[string][]*multipart.FileHeader, errors Errors) {

inputFieldName := typeField.Tag.Get("form")
if inputFieldName == "" {
return
}

inputFile, exists := formfile[inputFieldName]
if !exists {
return
}
fhType := reflect.TypeOf((*multipart.FileHeader)(nil))
numElems := len(inputFile)
if structField.Kind() == reflect.Slice && numElems > 0 && structField.Type().Elem() == fhType {
slice := reflect.MakeSlice(structField.Type(), numElems, numElems)
for i := 0; i < numElems; i++ {
slice.Index(i).Set(reflect.ValueOf(inputFile[i]))
}
structField.Set(slice)
} else if structField.Type() == fhType {
structField.Set(reflect.ValueOf(inputFile[0]))
}
}