gotosocial/vendor/github.com/go-playground/form/v4/encoder.go
zowhoey f518f649f8
[feature] Add support for profile fields (#1483)
* Add go-playground/form pkg

* [feature] Add support for profile fields

* Add field attributes test

* Validate profile fields form

* Add profile field validation tests

* Add Field Attributes definition to swagger

---------

Co-authored-by: tobi <31960611+tsmethurst@users.noreply.github.com>
2023-03-06 10:30:19 +01:00

262 lines
5.6 KiB
Go

package form
import (
"fmt"
"net/url"
"reflect"
"strconv"
"time"
)
type encoder struct {
e *Encoder
errs EncodeErrors
values url.Values
namespace []byte
}
func (e *encoder) setError(namespace []byte, err error) {
if e.errs == nil {
e.errs = make(EncodeErrors)
}
e.errs[string(namespace)] = err
}
func (e *encoder) setVal(namespace []byte, idx int, vals ...string) {
arr, ok := e.values[string(namespace)]
if ok {
arr = append(arr, vals...)
} else {
arr = vals
}
e.values[string(namespace)] = arr
}
func (e *encoder) traverseStruct(v reflect.Value, namespace []byte, idx int) {
typ := v.Type()
l := len(namespace)
first := l == 0
// anonymous structs will still work for caching as the whole definition is stored
// including tags
s, ok := e.e.structCache.Get(typ)
if !ok {
s = e.e.structCache.parseStruct(e.e.mode, v, typ, e.e.tagName)
}
for _, f := range s.fields {
namespace = namespace[:l]
if f.isAnonymous && e.e.embedAnonymous {
e.setFieldByType(v.Field(f.idx), namespace, idx, f.isOmitEmpty)
continue
}
if first {
namespace = append(namespace, f.name...)
} else {
namespace = append(namespace, e.e.namespacePrefix...)
namespace = append(namespace, f.name...)
namespace = append(namespace, e.e.namespaceSuffix...)
}
e.setFieldByType(v.Field(f.idx), namespace, idx, f.isOmitEmpty)
}
}
func (e *encoder) setFieldByType(current reflect.Value, namespace []byte, idx int, isOmitEmpty bool) {
if idx > -1 && current.Kind() == reflect.Ptr {
namespace = append(namespace, '[')
namespace = strconv.AppendInt(namespace, int64(idx), 10)
namespace = append(namespace, ']')
idx = -2
}
if isOmitEmpty && !hasValue(current) {
return
}
v, kind := ExtractType(current)
if e.e.customTypeFuncs != nil {
if cf, ok := e.e.customTypeFuncs[v.Type()]; ok {
arr, err := cf(v.Interface())
if err != nil {
e.setError(namespace, err)
return
}
if idx > -1 {
namespace = append(namespace, '[')
namespace = strconv.AppendInt(namespace, int64(idx), 10)
namespace = append(namespace, ']')
}
e.setVal(namespace, idx, arr...)
return
}
}
switch kind {
case reflect.Ptr, reflect.Interface, reflect.Invalid:
return
case reflect.String:
e.setVal(namespace, idx, v.String())
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
e.setVal(namespace, idx, strconv.FormatUint(v.Uint(), 10))
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
e.setVal(namespace, idx, strconv.FormatInt(v.Int(), 10))
case reflect.Float32:
e.setVal(namespace, idx, strconv.FormatFloat(v.Float(), 'f', -1, 32))
case reflect.Float64:
e.setVal(namespace, idx, strconv.FormatFloat(v.Float(), 'f', -1, 64))
case reflect.Bool:
e.setVal(namespace, idx, strconv.FormatBool(v.Bool()))
case reflect.Slice, reflect.Array:
if idx == -1 {
for i := 0; i < v.Len(); i++ {
e.setFieldByType(v.Index(i), namespace, i, false)
}
return
}
if idx > -1 {
namespace = append(namespace, '[')
namespace = strconv.AppendInt(namespace, int64(idx), 10)
namespace = append(namespace, ']')
}
namespace = append(namespace, '[')
l := len(namespace)
for i := 0; i < v.Len(); i++ {
namespace = namespace[:l]
namespace = strconv.AppendInt(namespace, int64(i), 10)
namespace = append(namespace, ']')
e.setFieldByType(v.Index(i), namespace, -2, false)
}
case reflect.Map:
if idx > -1 {
namespace = append(namespace, '[')
namespace = strconv.AppendInt(namespace, int64(idx), 10)
namespace = append(namespace, ']')
}
var valid bool
var s string
l := len(namespace)
for _, key := range v.MapKeys() {
namespace = namespace[:l]
if s, valid = e.getMapKey(key, namespace); !valid {
continue
}
namespace = append(namespace, '[')
namespace = append(namespace, s...)
namespace = append(namespace, ']')
e.setFieldByType(v.MapIndex(key), namespace, -2, false)
}
case reflect.Struct:
// if we get here then no custom time function declared so use RFC3339 by default
if v.Type() == timeType {
if idx > -1 {
namespace = append(namespace, '[')
namespace = strconv.AppendInt(namespace, int64(idx), 10)
namespace = append(namespace, ']')
}
e.setVal(namespace, idx, v.Interface().(time.Time).Format(time.RFC3339))
return
}
if idx == -1 {
e.traverseStruct(v, namespace, idx)
return
}
if idx > -1 {
namespace = append(namespace, '[')
namespace = strconv.AppendInt(namespace, int64(idx), 10)
namespace = append(namespace, ']')
}
e.traverseStruct(v, namespace, -2)
}
}
func (e *encoder) getMapKey(key reflect.Value, namespace []byte) (string, bool) {
v, kind := ExtractType(key)
if e.e.customTypeFuncs != nil {
if cf, ok := e.e.customTypeFuncs[v.Type()]; ok {
arr, err := cf(v.Interface())
if err != nil {
e.setError(namespace, err)
return "", false
}
return arr[0], true
}
}
switch kind {
case reflect.Interface, reflect.Ptr:
return "", false
case reflect.String:
return v.String(), true
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return strconv.FormatUint(v.Uint(), 10), true
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return strconv.FormatInt(v.Int(), 10), true
case reflect.Float32:
return strconv.FormatFloat(v.Float(), 'f', -1, 32), true
case reflect.Float64:
return strconv.FormatFloat(v.Float(), 'f', -1, 64), true
case reflect.Bool:
return strconv.FormatBool(v.Bool()), true
default:
e.setError(namespace, fmt.Errorf("Unsupported Map Key '%v' Namespace '%s'", v.String(), namespace))
return "", false
}
}