finclip-app-manager/vendor/github.com/go-playground/form/form_decoder.go

175 lines
4.4 KiB
Go
Raw Permalink Normal View History

2023-11-02 18:36:36 +08:00
package form
import (
"bytes"
"net/url"
"reflect"
"strings"
"sync"
)
// DecodeCustomTypeFunc allows for registering/overriding types to be parsed.
type DecodeCustomTypeFunc func([]string) (interface{}, error)
// DecodeErrors is a map of errors encountered during form decoding
type DecodeErrors map[string]error
func (d DecodeErrors) Error() string {
buff := bytes.NewBufferString(blank)
for k, err := range d {
buff.WriteString(fieldNS)
buff.WriteString(k)
buff.WriteString(errorText)
buff.WriteString(err.Error())
buff.WriteString("\n")
}
return strings.TrimSpace(buff.String())
}
// An InvalidDecoderError describes an invalid argument passed to Decode.
// (The argument passed to Decode must be a non-nil pointer.)
type InvalidDecoderError struct {
Type reflect.Type
}
func (e *InvalidDecoderError) Error() string {
if e.Type == nil {
return "form: Decode(nil)"
}
if e.Type.Kind() != reflect.Ptr {
return "form: Decode(non-pointer " + e.Type.String() + ")"
}
return "form: Decode(nil " + e.Type.String() + ")"
}
type key struct {
ivalue int
value string
searchValue string
}
type recursiveData struct {
alias string
sliceLen int
keys []key
}
type dataMap []*recursiveData
// Decoder is the main decode instance
type Decoder struct {
tagName string
mode Mode
structCache *structCacheMap
customTypeFuncs map[reflect.Type]DecodeCustomTypeFunc
maxArraySize int
dataPool *sync.Pool
}
// NewDecoder creates a new decoder instance with sane defaults
func NewDecoder() *Decoder {
d := &Decoder{
tagName: "form",
mode: ModeImplicit,
structCache: newStructCacheMap(),
maxArraySize: 10000,
}
d.dataPool = &sync.Pool{New: func() interface{} {
return &decoder{
d: d,
namespace: make([]byte, 0, 64),
}
}}
return d
}
// SetTagName sets the given tag name to be used by the decoder.
// Default is "form"
func (d *Decoder) SetTagName(tagName string) {
d.tagName = tagName
}
// SetMode sets the mode the decoder should run
// Default is ModeImplicit
func (d *Decoder) SetMode(mode Mode) {
d.mode = mode
}
// SetMaxArraySize sets maximum array size that can be created.
// This limit is for the array indexing this library supports to
// avoid potential DOS or man-in-the-middle attacks using an unusually
// high number.
// DEFAULT: 10000
func (d *Decoder) SetMaxArraySize(size uint) {
d.maxArraySize = int(size)
}
// RegisterTagNameFunc registers a custom tag name parser function
// NOTE: This method is not thread-safe it is intended that these all be registered prior to any parsing
//
// ADDITIONAL: once a custom function has been registered the default, or custom set, tag name is ignored
// and relies 100% on the function for the name data. The return value WILL BE CACHED and so return value
// must be consistent.
func (d *Decoder) RegisterTagNameFunc(fn TagNameFunc) {
d.structCache.tagFn = fn
}
// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types.
// NOTE: This method is not thread-safe it is intended that these all be registered prior to any parsing
//
// ADDITIONAL: if a struct type is registered, the function will only be called if a url.Value exists for
// the struct and not just the struct fields eg. url.Values{"User":"Name%3Djoeybloggs"} will call the
// custom type function with `User` as the type, however url.Values{"User.Name":"joeybloggs"} will not.
func (d *Decoder) RegisterCustomTypeFunc(fn DecodeCustomTypeFunc, types ...interface{}) {
if d.customTypeFuncs == nil {
d.customTypeFuncs = map[reflect.Type]DecodeCustomTypeFunc{}
}
for _, t := range types {
d.customTypeFuncs[reflect.TypeOf(t)] = fn
}
}
// Decode parses the given values and sets the corresponding struct and/or type values
//
// Decode returns an InvalidDecoderError if interface passed is invalid.
func (d *Decoder) Decode(v interface{}, values url.Values) (err error) {
val := reflect.ValueOf(v)
if val.Kind() != reflect.Ptr || val.IsNil() {
return &InvalidDecoderError{reflect.TypeOf(v)}
}
dec := d.dataPool.Get().(*decoder)
dec.values = values
dec.dm = dec.dm[0:0]
val = val.Elem()
typ := val.Type()
if val.Kind() == reflect.Struct && typ != timeType {
dec.traverseStruct(val, typ, dec.namespace[0:0])
} else {
dec.setFieldByType(val, dec.namespace[0:0], 0)
}
if len(dec.errs) > 0 {
err = dec.errs
dec.errs = nil
}
d.dataPool.Put(dec)
return
}