summaryrefslogtreecommitdiff
path: root/vendor/github.com/go-playground/form/v4
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/go-playground/form/v4')
-rw-r--r--vendor/github.com/go-playground/form/v4/.gitignore28
-rw-r--r--vendor/github.com/go-playground/form/v4/.travis.yml26
-rw-r--r--vendor/github.com/go-playground/form/v4/LICENSE21
-rw-r--r--vendor/github.com/go-playground/form/v4/README.md333
-rw-r--r--vendor/github.com/go-playground/form/v4/cache.go133
-rw-r--r--vendor/github.com/go-playground/form/v4/decoder.go748
-rw-r--r--vendor/github.com/go-playground/form/v4/doc.go275
-rw-r--r--vendor/github.com/go-playground/form/v4/encoder.go261
-rw-r--r--vendor/github.com/go-playground/form/v4/form.go49
-rw-r--r--vendor/github.com/go-playground/form/v4/form_decoder.go187
-rw-r--r--vendor/github.com/go-playground/form/v4/form_encoder.go157
-rw-r--r--vendor/github.com/go-playground/form/v4/logo.jpgbin0 -> 20388 bytes
-rw-r--r--vendor/github.com/go-playground/form/v4/util.go62
13 files changed, 2280 insertions, 0 deletions
diff --git a/vendor/github.com/go-playground/form/v4/.gitignore b/vendor/github.com/go-playground/form/v4/.gitignore
new file mode 100644
index 000000000..aa19ee287
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/.gitignore
@@ -0,0 +1,28 @@
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# Folders
+_obj
+_test
+
+# Architecture specific extensions/prefixes
+*.[568vq]
+[568vq].out
+
+*.cgo1.go
+*.cgo2.c
+_cgo_defun.c
+_cgo_gotypes.go
+_cgo_export.*
+
+_testmain.go
+
+*.exe
+*.test
+*.prof
+old.txt
+new.txt
+
+/.idea \ No newline at end of file
diff --git a/vendor/github.com/go-playground/form/v4/.travis.yml b/vendor/github.com/go-playground/form/v4/.travis.yml
new file mode 100644
index 000000000..fbc61d1b8
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/.travis.yml
@@ -0,0 +1,26 @@
+language: go
+go:
+ - 1.13.4
+ - tip
+matrix:
+ allow_failures:
+ - go: tip
+
+notifications:
+ email:
+ recipients: dean.karn@gmail.com
+ on_success: change
+ on_failure: always
+
+before_install:
+ - go install github.com/mattn/goveralls
+
+# Only clone the most recent commit.
+git:
+ depth: 1
+
+script:
+ - go test -v -race -covermode=atomic -coverprofile=coverage.coverprofile ./...
+
+after_success: |
+ goveralls -coverprofile=coverage.coverprofile -service travis-ci -repotoken $COVERALLS_TOKEN \ No newline at end of file
diff --git a/vendor/github.com/go-playground/form/v4/LICENSE b/vendor/github.com/go-playground/form/v4/LICENSE
new file mode 100644
index 000000000..8d8aba15b
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Go Playground
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/go-playground/form/v4/README.md b/vendor/github.com/go-playground/form/v4/README.md
new file mode 100644
index 000000000..647100f6f
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/README.md
@@ -0,0 +1,333 @@
+Package form
+============
+<img align="right" src="https://raw.githubusercontent.com/go-playground/form/master/logo.jpg">![Project status](https://img.shields.io/badge/version-4.2.0-green.svg)
+[![Build Status](https://github.com/go-playground/form/actions/workflows/workflow.yml/badge.svg)](https://github.com/go-playground/form/actions/workflows/workflow.yml)
+[![Coverage Status](https://coveralls.io/repos/github/go-playground/form/badge.svg?branch=master)](https://coveralls.io/github/go-playground/form?branch=master)
+[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/form)](https://goreportcard.com/report/github.com/go-playground/form)
+[![GoDoc](https://godoc.org/github.com/go-playground/form?status.svg)](https://godoc.org/github.com/go-playground/form)
+![License](https://img.shields.io/dub/l/vibe-d.svg)
+[![Gitter](https://badges.gitter.im/go-playground/form.svg)](https://gitter.im/go-playground/form?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
+
+Package form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values.
+
+It has the following features:
+
+- Supports map of almost all types.
+- Supports both Numbered and Normal arrays eg. `"Array[0]"` and just `"Array"` with multiple values passed.
+- Slice honours the specified index. eg. if "Slice[2]" is the only Slice value passed down, it will be put at index 2; if slice isn't big enough it will be expanded.
+- Array honours the specified index. eg. if "Array[2]" is the only Array value passed down, it will be put at index 2; if array isn't big enough a warning will be printed and value ignored.
+- Only creates objects as necessary eg. if no `array` or `map` values are passed down, the `array` and `map` are left as their default values in the struct.
+- Allows for Custom Type registration.
+- Handles time.Time using RFC3339 time format by default, but can easily be changed by registering a Custom Type, see below.
+- Handles Encoding & Decoding of almost all Go types eg. can Decode into struct, array, map, int... and Encode a struct, array, map, int...
+
+Common Questions
+
+- Does it support encoding.TextUnmarshaler? No because TextUnmarshaler only accepts []byte but posted values can have multiple values, so is not suitable.
+- Mixing `array/slice` with `array[idx]/slice[idx]`, in which order are they parsed? `array/slice` then `array[idx]/slice[idx]`
+
+Supported Types ( out of the box )
+----------
+
+* `string`
+* `bool`
+* `int`, `int8`, `int16`, `int32`, `int64`
+* `uint`, `uint8`, `uint16`, `uint32`, `uint64`
+* `float32`, `float64`
+* `struct` and `anonymous struct`
+* `interface{}`
+* `time.Time` - by default using RFC3339
+* a `pointer` to one of the above types
+* `slice`, `array`
+* `map`
+* `custom types` can override any of the above types
+* many other types may be supported inherently
+
+**NOTE**: `map`, `struct` and `slice` nesting are ad infinitum.
+
+Installation
+------------
+
+Use go get.
+
+ go get github.com/go-playground/form
+
+Then import the form package into your own code.
+
+ import "github.com/go-playground/form/v4"
+
+Usage
+-----
+
+- Use symbol `.` for separating fields/structs. (eg. `structfield.field`)
+- Use `[index or key]` for access to index of a slice/array or key for map. (eg. `arrayfield[0]`, `mapfield[keyvalue]`)
+
+```html
+<form method="POST">
+ <input type="text" name="Name" value="joeybloggs"/>
+ <input type="text" name="Age" value="3"/>
+ <input type="text" name="Gender" value="Male"/>
+ <input type="text" name="Address[0].Name" value="26 Here Blvd."/>
+ <input type="text" name="Address[0].Phone" value="9(999)999-9999"/>
+ <input type="text" name="Address[1].Name" value="26 There Blvd."/>
+ <input type="text" name="Address[1].Phone" value="1(111)111-1111"/>
+ <input type="text" name="active" value="true"/>
+ <input type="text" name="MapExample[key]" value="value"/>
+ <input type="text" name="NestedMap[key][key]" value="value"/>
+ <input type="text" name="NestedArray[0][0]" value="value"/>
+ <input type="submit"/>
+</form>
+```
+
+Examples
+-------
+
+Decoding
+```go
+package main
+
+import (
+ "fmt"
+ "log"
+ "net/url"
+
+ "github.com/go-playground/form/v4"
+)
+
+// Address contains address information
+type Address struct {
+ Name string
+ Phone string
+}
+
+// User contains user information
+type User struct {
+ Name string
+ Age uint8
+ Gender string
+ Address []Address
+ Active bool `form:"active"`
+ MapExample map[string]string
+ NestedMap map[string]map[string]string
+ NestedArray [][]string
+}
+
+// use a single instance of Decoder, it caches struct info
+var decoder *form.Decoder
+
+func main() {
+ decoder = form.NewDecoder()
+
+ // this simulates the results of http.Request's ParseForm() function
+ values := parseForm()
+
+ var user User
+
+ // must pass a pointer
+ err := decoder.Decode(&user, values)
+ if err != nil {
+ log.Panic(err)
+ }
+
+ fmt.Printf("%#v\n", user)
+}
+
+// this simulates the results of http.Request's ParseForm() function
+func parseForm() url.Values {
+ return url.Values{
+ "Name": []string{"joeybloggs"},
+ "Age": []string{"3"},
+ "Gender": []string{"Male"},
+ "Address[0].Name": []string{"26 Here Blvd."},
+ "Address[0].Phone": []string{"9(999)999-9999"},
+ "Address[1].Name": []string{"26 There Blvd."},
+ "Address[1].Phone": []string{"1(111)111-1111"},
+ "active": []string{"true"},
+ "MapExample[key]": []string{"value"},
+ "NestedMap[key][key]": []string{"value"},
+ "NestedArray[0][0]": []string{"value"},
+ }
+}
+```
+
+Encoding
+```go
+package main
+
+import (
+ "fmt"
+ "log"
+
+ "github.com/go-playground/form/v4"
+)
+
+// Address contains address information
+type Address struct {
+ Name string
+ Phone string
+}
+
+// User contains user information
+type User struct {
+ Name string
+ Age uint8
+ Gender string
+ Address []Address
+ Active bool `form:"active"`
+ MapExample map[string]string
+ NestedMap map[string]map[string]string
+ NestedArray [][]string
+}
+
+// use a single instance of Encoder, it caches struct info
+var encoder *form.Encoder
+
+func main() {
+ encoder = form.NewEncoder()
+
+ user := User{
+ Name: "joeybloggs",
+ Age: 3,
+ Gender: "Male",
+ Address: []Address{
+ {Name: "26 Here Blvd.", Phone: "9(999)999-9999"},
+ {Name: "26 There Blvd.", Phone: "1(111)111-1111"},
+ },
+ Active: true,
+ MapExample: map[string]string{"key": "value"},
+ NestedMap: map[string]map[string]string{"key": {"key": "value"}},
+ NestedArray: [][]string{{"value"}},
+ }
+
+ // must pass a pointer
+ values, err := encoder.Encode(&user)
+ if err != nil {
+ log.Panic(err)
+ }
+
+ fmt.Printf("%#v\n", values)
+}
+```
+
+Registering Custom Types
+--------------
+
+Decoder
+```go
+decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
+ return time.Parse("2006-01-02", vals[0])
+}, time.Time{})
+```
+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.
+
+
+Encoder
+```go
+encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
+ return []string{x.(time.Time).Format("2006-01-02")}, nil
+}, time.Time{})
+```
+
+Ignoring Fields
+--------------
+you can tell form to ignore fields using `-` in the tag
+```go
+type MyStruct struct {
+ Field string `form:"-"`
+}
+```
+
+Omitempty
+--------------
+you can tell form to omit empty fields using `,omitempty` or `FieldName,omitempty` in the tag
+```go
+type MyStruct struct {
+ Field string `form:",omitempty"`
+ Field2 string `form:"CustomFieldName,omitempty"`
+}
+```
+
+Notes
+------
+To maximize compatibility with other systems the Encoder attempts
+to avoid using array indexes in url.Values if at all possible.
+
+eg.
+```go
+// A struct field of
+Field []string{"1", "2", "3"}
+
+// will be output a url.Value as
+"Field": []string{"1", "2", "3"}
+
+and not
+"Field[0]": []string{"1"}
+"Field[1]": []string{"2"}
+"Field[2]": []string{"3"}
+
+// however there are times where it is unavoidable, like with pointers
+i := int(1)
+Field []*string{nil, nil, &i}
+
+// to avoid index 1 and 2 must use index
+"Field[2]": []string{"1"}
+```
+
+Benchmarks
+------
+###### Run on MacBook Pro (15-inch, 2017) using go version go1.10.1 darwin/amd64
+
+NOTE: the 1 allocation and B/op in the first 4 decodes is actually the struct allocating when passing it in, so primitives are actually zero allocation.
+
+```go
+go test -run=NONE -bench=. -benchmem=true
+goos: darwin
+goarch: amd64
+pkg: github.com/go-playground/form/benchmarks
+
+BenchmarkSimpleUserDecodeStruct-8 5000000 236 ns/op 64 B/op 1 allocs/op
+BenchmarkSimpleUserDecodeStructParallel-8 20000000 82.1 ns/op 64 B/op 1 allocs/op
+BenchmarkSimpleUserEncodeStruct-8 2000000 627 ns/op 485 B/op 10 allocs/op
+BenchmarkSimpleUserEncodeStructParallel-8 10000000 223 ns/op 485 B/op 10 allocs/op
+BenchmarkPrimitivesDecodeStructAllPrimitivesTypes-8 2000000 724 ns/op 96 B/op 1 allocs/op
+BenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallel-8 10000000 246 ns/op 96 B/op 1 allocs/op
+BenchmarkPrimitivesEncodeStructAllPrimitivesTypes-8 500000 3187 ns/op 2977 B/op 36 allocs/op
+BenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallel-8 1000000 1106 ns/op 2977 B/op 36 allocs/op
+BenchmarkComplexArrayDecodeStructAllTypes-8 100000 13748 ns/op 2248 B/op 121 allocs/op
+BenchmarkComplexArrayDecodeStructAllTypesParallel-8 500000 4313 ns/op 2249 B/op 121 allocs/op
+BenchmarkComplexArrayEncodeStructAllTypes-8 200000 10758 ns/op 7113 B/op 104 allocs/op
+BenchmarkComplexArrayEncodeStructAllTypesParallel-8 500000 3532 ns/op 7113 B/op 104 allocs/op
+BenchmarkComplexMapDecodeStructAllTypes-8 100000 17644 ns/op 5305 B/op 130 allocs/op
+BenchmarkComplexMapDecodeStructAllTypesParallel-8 300000 5470 ns/op 5308 B/op 130 allocs/op
+BenchmarkComplexMapEncodeStructAllTypes-8 200000 11155 ns/op 6971 B/op 129 allocs/op
+BenchmarkComplexMapEncodeStructAllTypesParallel-8 500000 3768 ns/op 6971 B/op 129 allocs/op
+BenchmarkDecodeNestedStruct-8 500000 2462 ns/op 384 B/op 14 allocs/op
+BenchmarkDecodeNestedStructParallel-8 2000000 814 ns/op 384 B/op 14 allocs/op
+BenchmarkEncodeNestedStruct-8 1000000 1483 ns/op 693 B/op 16 allocs/op
+BenchmarkEncodeNestedStructParallel-8 3000000 525 ns/op 693 B/op 16 allocs/op
+```
+
+Competitor benchmarks can be found [here](https://github.com/go-playground/form/blob/master/benchmarks/benchmarks.md)
+
+Complimentary Software
+----------------------
+
+Here is a list of software that compliments using this library post decoding.
+
+* [Validator](https://github.com/go-playground/validator) - Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving.
+* [mold](https://github.com/go-playground/mold) - Is a general library to help modify or set data within data structures and other objects.
+
+Package Versioning
+----------
+I'm jumping on the vendoring bandwagon, you should vendor this package as I will not
+be creating different version with gopkg.in like allot of my other libraries.
+
+Why? because my time is spread pretty thin maintaining all of the libraries I have + LIFE,
+it is so freeing not to worry about it and will help me keep pouring out bigger and better
+things for you the community.
+
+License
+------
+Distributed under MIT License, please see license file in code for more details.
diff --git a/vendor/github.com/go-playground/form/v4/cache.go b/vendor/github.com/go-playground/form/v4/cache.go
new file mode 100644
index 000000000..47e5fce96
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/cache.go
@@ -0,0 +1,133 @@
+package form
+
+import (
+ "reflect"
+ "sort"
+ "strings"
+ "sync"
+ "sync/atomic"
+)
+
+type cacheFields []cachedField
+
+func (s cacheFields) Len() int {
+ return len(s)
+}
+
+func (s cacheFields) Less(i, j int) bool {
+ return !s[i].isAnonymous
+}
+
+func (s cacheFields) Swap(i, j int) {
+ s[i], s[j] = s[j], s[i]
+}
+
+type cachedField struct {
+ idx int
+ name string
+ isAnonymous bool
+ isOmitEmpty bool
+}
+
+type cachedStruct struct {
+ fields cacheFields
+}
+
+type structCacheMap struct {
+ m atomic.Value // map[reflect.Type]*cachedStruct
+ lock sync.Mutex
+ tagFn TagNameFunc
+}
+
+// TagNameFunc allows for adding of a custom tag name parser
+type TagNameFunc func(field reflect.StructField) string
+
+func newStructCacheMap() *structCacheMap {
+
+ sc := new(structCacheMap)
+ sc.m.Store(make(map[reflect.Type]*cachedStruct))
+
+ return sc
+}
+
+func (s *structCacheMap) Get(key reflect.Type) (value *cachedStruct, ok bool) {
+ value, ok = s.m.Load().(map[reflect.Type]*cachedStruct)[key]
+ return
+}
+
+func (s *structCacheMap) Set(key reflect.Type, value *cachedStruct) {
+
+ m := s.m.Load().(map[reflect.Type]*cachedStruct)
+
+ nm := make(map[reflect.Type]*cachedStruct, len(m)+1)
+ for k, v := range m {
+ nm[k] = v
+ }
+ nm[key] = value
+ s.m.Store(nm)
+}
+
+func (s *structCacheMap) parseStruct(mode Mode, current reflect.Value, key reflect.Type, tagName string) *cachedStruct {
+
+ s.lock.Lock()
+
+ // could have been multiple trying to access, but once first is done this ensures struct
+ // isn't parsed again.
+ cs, ok := s.Get(key)
+ if ok {
+ s.lock.Unlock()
+ return cs
+ }
+
+ typ := current.Type()
+ cs = &cachedStruct{fields: make([]cachedField, 0, 4)} // init 4, betting most structs decoding into have at aleast 4 fields.
+
+ numFields := current.NumField()
+
+ var fld reflect.StructField
+ var name string
+ var idx int
+ var isOmitEmpty bool
+
+ for i := 0; i < numFields; i++ {
+ isOmitEmpty = false
+ fld = typ.Field(i)
+
+ if fld.PkgPath != blank && !fld.Anonymous {
+ continue
+ }
+
+ if s.tagFn != nil {
+ name = s.tagFn(fld)
+ } else {
+ name = fld.Tag.Get(tagName)
+ }
+
+ if name == ignore {
+ continue
+ }
+
+ if mode == ModeExplicit && len(name) == 0 {
+ continue
+ }
+
+ // check for omitempty
+ if idx = strings.LastIndexByte(name, ','); idx != -1 {
+ isOmitEmpty = name[idx+1:] == "omitempty"
+ name = name[:idx]
+ }
+
+ if len(name) == 0 {
+ name = fld.Name
+ }
+
+ cs.fields = append(cs.fields, cachedField{idx: i, name: name, isAnonymous: fld.Anonymous, isOmitEmpty: isOmitEmpty})
+ }
+
+ sort.Sort(cs.fields)
+ s.Set(typ, cs)
+
+ s.lock.Unlock()
+
+ return cs
+}
diff --git a/vendor/github.com/go-playground/form/v4/decoder.go b/vendor/github.com/go-playground/form/v4/decoder.go
new file mode 100644
index 000000000..e71c97794
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/decoder.go
@@ -0,0 +1,748 @@
+package form
+
+import (
+ "fmt"
+ "log"
+ "net/url"
+ "reflect"
+ "strconv"
+ "time"
+)
+
+const (
+ errArraySize = "Array size of '%d' is larger than the maximum currently set on the decoder of '%d'. To increase this limit please see, SetMaxArraySize(size uint)"
+ errMissingStartBracket = "Invalid formatting for key '%s' missing '[' bracket"
+ errMissingEndBracket = "Invalid formatting for key '%s' missing ']' bracket"
+)
+
+type decoder struct {
+ d *Decoder
+ errs DecodeErrors
+ dm dataMap
+ values url.Values
+ maxKeyLen int
+ namespace []byte
+}
+
+func (d *decoder) setError(namespace []byte, err error) {
+ if d.errs == nil {
+ d.errs = make(DecodeErrors)
+ }
+ d.errs[string(namespace)] = err
+}
+
+func (d *decoder) findAlias(ns string) *recursiveData {
+ for i := 0; i < len(d.dm); i++ {
+ if d.dm[i].alias == ns {
+ return d.dm[i]
+ }
+ }
+ return nil
+}
+
+func (d *decoder) parseMapData() {
+ // already parsed
+ if len(d.dm) > 0 {
+ return
+ }
+
+ d.maxKeyLen = 0
+ d.dm = d.dm[0:0]
+
+ var i int
+ var idx int
+ var l int
+ var insideBracket bool
+ var rd *recursiveData
+ var isNum bool
+
+ for k := range d.values {
+
+ if len(k) > d.maxKeyLen {
+ d.maxKeyLen = len(k)
+ }
+
+ for i = 0; i < len(k); i++ {
+
+ switch k[i] {
+ case '[':
+ idx = i
+ insideBracket = true
+ isNum = true
+ case ']':
+
+ if !insideBracket {
+ log.Panicf(errMissingStartBracket, k)
+ }
+
+ if rd = d.findAlias(k[:idx]); rd == nil {
+
+ l = len(d.dm) + 1
+
+ if l > cap(d.dm) {
+ dm := make(dataMap, l)
+ copy(dm, d.dm)
+ rd = new(recursiveData)
+ dm[len(d.dm)] = rd
+ d.dm = dm
+ } else {
+ l = len(d.dm)
+ d.dm = d.dm[:l+1]
+ rd = d.dm[l]
+ rd.sliceLen = 0
+ rd.keys = rd.keys[0:0]
+ }
+
+ rd.alias = k[:idx]
+ }
+
+ // is map + key
+ ke := key{
+ ivalue: -1,
+ value: k[idx+1 : i],
+ searchValue: k[idx : i+1],
+ }
+
+ // is key is number, most likely array key, keep track of just in case an array/slice.
+ if isNum {
+
+ // no need to check for error, it will always pass
+ // as we have done the checking to ensure
+ // the value is a number ahead of time.
+ ke.ivalue, _ = strconv.Atoi(ke.value)
+
+ if ke.ivalue > rd.sliceLen {
+ rd.sliceLen = ke.ivalue
+
+ }
+ }
+
+ rd.keys = append(rd.keys, ke)
+
+ insideBracket = false
+ default:
+ // checking if not a number, 0-9 is 48-57 in byte, see for yourself fmt.Println('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
+ if insideBracket && (k[i] > 57 || k[i] < 48) {
+ isNum = false
+ }
+ }
+ }
+
+ // if still inside bracket, that means no ending bracket was ever specified
+ if insideBracket {
+ log.Panicf(errMissingEndBracket, k)
+ }
+ }
+}
+
+func (d *decoder) traverseStruct(v reflect.Value, typ reflect.Type, namespace []byte) (set bool) {
+
+ l := len(namespace)
+ first := l == 0
+
+ // anonymous structs will still work for caching as the whole definition is stored
+ // including tags
+ s, ok := d.d.structCache.Get(typ)
+ if !ok {
+ s = d.d.structCache.parseStruct(d.d.mode, v, typ, d.d.tagName)
+ }
+
+ for _, f := range s.fields {
+ namespace = namespace[:l]
+
+ if f.isAnonymous {
+ if d.setFieldByType(v.Field(f.idx), namespace, 0) {
+ set = true
+ }
+ }
+
+ if first {
+ namespace = append(namespace, f.name...)
+ } else {
+ namespace = append(namespace, d.d.namespacePrefix...)
+ namespace = append(namespace, f.name...)
+ namespace = append(namespace, d.d.namespaceSuffix...)
+ }
+
+ if d.setFieldByType(v.Field(f.idx), namespace, 0) {
+ set = true
+ }
+ }
+
+ return
+}
+
+func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx int) (set bool) {
+
+ var err error
+ v, kind := ExtractType(current)
+
+ arr, ok := d.values[string(namespace)]
+
+ if d.d.customTypeFuncs != nil {
+
+ if ok {
+ if cf, ok := d.d.customTypeFuncs[v.Type()]; ok {
+ val, err := cf(arr[idx:])
+ if err != nil {
+ d.setError(namespace, err)
+ return
+ }
+
+ v.Set(reflect.ValueOf(val))
+ set = true
+ return
+ }
+ }
+ }
+ switch kind {
+ case reflect.Interface:
+ if !ok || idx == len(arr) {
+ return
+ }
+ v.Set(reflect.ValueOf(arr[idx]))
+ set = true
+
+ case reflect.Ptr:
+ newVal := reflect.New(v.Type().Elem())
+ if set = d.setFieldByType(newVal.Elem(), namespace, idx); set {
+ v.Set(newVal)
+ }
+
+ case reflect.String:
+ if !ok || idx == len(arr) {
+ return
+ }
+ v.SetString(arr[idx])
+ set = true
+
+ case reflect.Uint, reflect.Uint64:
+ if !ok || idx == len(arr) || len(arr[idx]) == 0 {
+ return
+ }
+ var u64 uint64
+ if u64, err = strconv.ParseUint(arr[idx], 10, 64); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetUint(u64)
+ set = true
+
+ case reflect.Uint8:
+ if !ok || idx == len(arr) || len(arr[idx]) == 0 {
+ return
+ }
+ var u64 uint64
+ if u64, err = strconv.ParseUint(arr[idx], 10, 8); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetUint(u64)
+ set = true
+
+ case reflect.Uint16:
+ if !ok || idx == len(arr) || len(arr[idx]) == 0 {
+ return
+ }
+ var u64 uint64
+ if u64, err = strconv.ParseUint(arr[idx], 10, 16); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetUint(u64)
+ set = true
+
+ case reflect.Uint32:
+ if !ok || idx == len(arr) || len(arr[idx]) == 0 {
+ return
+ }
+ var u64 uint64
+ if u64, err = strconv.ParseUint(arr[idx], 10, 32); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetUint(u64)
+ set = true
+
+ case reflect.Int, reflect.Int64:
+ if !ok || idx == len(arr) || len(arr[idx]) == 0 {
+ return
+ }
+ var i64 int64
+ if i64, err = strconv.ParseInt(arr[idx], 10, 64); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetInt(i64)
+ set = true
+
+ case reflect.Int8:
+ if !ok || idx == len(arr) || len(arr[idx]) == 0 {
+ return
+ }
+ var i64 int64
+ if i64, err = strconv.ParseInt(arr[idx], 10, 8); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetInt(i64)
+ set = true
+
+ case reflect.Int16:
+ if !ok || idx == len(arr) || len(arr[idx]) == 0 {
+ return
+ }
+ var i64 int64
+ if i64, err = strconv.ParseInt(arr[idx], 10, 16); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetInt(i64)
+ set = true
+
+ case reflect.Int32:
+ if !ok || idx == len(arr) || len(arr[idx]) == 0 {
+ return
+ }
+ var i64 int64
+ if i64, err = strconv.ParseInt(arr[idx], 10, 32); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetInt(i64)
+ set = true
+
+ case reflect.Float32:
+ if !ok || idx == len(arr) || len(arr[idx]) == 0 {
+ return
+ }
+ var f float64
+ if f, err = strconv.ParseFloat(arr[idx], 32); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetFloat(f)
+ set = true
+
+ case reflect.Float64:
+ if !ok || idx == len(arr) || len(arr[idx]) == 0 {
+ return
+ }
+ var f float64
+ if f, err = strconv.ParseFloat(arr[idx], 64); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetFloat(f)
+ set = true
+
+ case reflect.Bool:
+ if !ok || idx == len(arr) {
+ return
+ }
+ var b bool
+ if b, err = parseBool(arr[idx]); err != nil {
+ d.setError(namespace, fmt.Errorf("Invalid Boolean Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
+ return
+ }
+ v.SetBool(b)
+ set = true
+
+ case reflect.Slice:
+ d.parseMapData()
+ // slice elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"}
+
+ if ok && len(arr) > 0 {
+ var varr reflect.Value
+
+ var ol int
+ l := len(arr)
+
+ if v.IsNil() {
+ varr = reflect.MakeSlice(v.Type(), len(arr), len(arr))
+ } else {
+
+ ol = v.Len()
+ l += ol
+
+ if v.Cap() <= l {
+ varr = reflect.MakeSlice(v.Type(), l, l)
+ } else {
+ // preserve predefined capacity, possibly for reuse after decoding
+ varr = reflect.MakeSlice(v.Type(), l, v.Cap())
+ }
+ reflect.Copy(varr, v)
+ }
+
+ for i := ol; i < l; i++ {
+ newVal := reflect.New(v.Type().Elem()).Elem()
+
+ if d.setFieldByType(newVal, namespace, i-ol) {
+ set = true
+ varr.Index(i).Set(newVal)
+ }
+ }
+
+ v.Set(varr)
+ }
+
+ // maybe it's an numbered array i.e. Phone[0].Number
+ if rd := d.findAlias(string(namespace)); rd != nil {
+
+ var varr reflect.Value
+ var kv key
+
+ sl := rd.sliceLen + 1
+
+ // checking below for maxArraySize, but if array exists and already
+ // has sufficient capacity allocated then we do not check as the code
+ // obviously allows a capacity greater than the maxArraySize.
+
+ if v.IsNil() {
+
+ if sl > d.d.maxArraySize {
+ d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize))
+ return
+ }
+
+ varr = reflect.MakeSlice(v.Type(), sl, sl)
+
+ } else if v.Len() < sl {
+
+ if v.Cap() <= sl {
+
+ if sl > d.d.maxArraySize {
+ d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize))
+ return
+ }
+
+ varr = reflect.MakeSlice(v.Type(), sl, sl)
+ } else {
+ varr = reflect.MakeSlice(v.Type(), sl, v.Cap())
+ }
+
+ reflect.Copy(varr, v)
+
+ } else {
+ varr = v
+ }
+
+ for i := 0; i < len(rd.keys); i++ {
+
+ kv = rd.keys[i]
+ newVal := reflect.New(varr.Type().Elem()).Elem()
+
+ if kv.ivalue == -1 {
+ d.setError(namespace, fmt.Errorf("invalid slice index '%s'", kv.value))
+ continue
+ }
+
+ if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
+ set = true
+ varr.Index(kv.ivalue).Set(newVal)
+ }
+ }
+
+ if !set {
+ return
+ }
+
+ v.Set(varr)
+ }
+
+ case reflect.Array:
+ d.parseMapData()
+
+ // array elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"}
+
+ if ok && len(arr) > 0 {
+ var varr reflect.Value
+ l := len(arr)
+ overCapacity := v.Len() < l
+ if overCapacity {
+ // more values than array capacity, ignore values over capacity as it's possible some would just want
+ // to grab the first x number of elements; in the future strict mode logic should return an error
+ fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values")
+ }
+ varr = reflect.Indirect(reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem())))
+ reflect.Copy(varr, v)
+
+ if v.Len() < len(arr) {
+ l = v.Len()
+ }
+ for i := 0; i < l; i++ {
+ newVal := reflect.New(v.Type().Elem()).Elem()
+
+ if d.setFieldByType(newVal, namespace, i) {
+ set = true
+ varr.Index(i).Set(newVal)
+ }
+ }
+ v.Set(varr)
+ }
+
+ // maybe it's an numbered array i.e. Phone[0].Number
+ if rd := d.findAlias(string(namespace)); rd != nil {
+ var varr reflect.Value
+ var kv key
+
+ overCapacity := rd.sliceLen >= v.Len()
+ if overCapacity {
+ // more values than array capacity, ignore values over capacity as it's possible some would just want
+ // to grab the first x number of elements; in the future strict mode logic should return an error
+ fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values")
+ }
+ varr = reflect.Indirect(reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem())))
+ reflect.Copy(varr, v)
+
+ for i := 0; i < len(rd.keys); i++ {
+ kv = rd.keys[i]
+ if kv.ivalue >= v.Len() {
+ continue
+ }
+ newVal := reflect.New(varr.Type().Elem()).Elem()
+
+ if kv.ivalue == -1 {
+ d.setError(namespace, fmt.Errorf("invalid array index '%s'", kv.value))
+ continue
+ }
+
+ if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
+ set = true
+ varr.Index(kv.ivalue).Set(newVal)
+ }
+ }
+
+ if !set {
+ return
+ }
+ v.Set(varr)
+ }
+
+ case reflect.Map:
+ var rd *recursiveData
+
+ d.parseMapData()
+
+ // no natural map support so skip directly to dm lookup
+ if rd = d.findAlias(string(namespace)); rd == nil {
+ return
+ }
+
+ var existing bool
+ var kv key
+ var mp reflect.Value
+ var mk reflect.Value
+
+ typ := v.Type()
+
+ if v.IsNil() {
+ mp = reflect.MakeMap(typ)
+ } else {
+ existing = true
+ mp = v
+ }
+
+ for i := 0; i < len(rd.keys); i++ {
+ newVal := reflect.New(typ.Elem()).Elem()
+ mk = reflect.New(typ.Key()).Elem()
+ kv = rd.keys[i]
+
+ if err := d.getMapKey(kv.value, mk, namespace); err != nil {
+ d.setError(namespace, err)
+ continue
+ }
+
+ if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
+ set = true
+ mp.SetMapIndex(mk, newVal)
+ }
+ }
+
+ if !set || existing {
+ return
+ }
+
+ v.Set(mp)
+
+ case reflect.Struct:
+ typ := v.Type()
+
+ // if we get here then no custom time function declared so use RFC3339 by default
+ if typ == timeType {
+
+ if !ok || len(arr[idx]) == 0 {
+ return
+ }
+
+ t, err := time.Parse(time.RFC3339, arr[idx])
+ if err != nil {
+ d.setError(namespace, err)
+ }
+
+ v.Set(reflect.ValueOf(t))
+ set = true
+ return
+ }
+
+ d.parseMapData()
+
+ // we must be recursing infinitly...but that's ok we caught it on the very first overun.
+ if len(namespace) > d.maxKeyLen {
+ return
+ }
+
+ set = d.traverseStruct(v, typ, namespace)
+ }
+ return
+}
+
+func (d *decoder) getMapKey(key string, current reflect.Value, namespace []byte) (err error) {
+
+ v, kind := ExtractType(current)
+
+ if d.d.customTypeFuncs != nil {
+ if cf, ok := d.d.customTypeFuncs[v.Type()]; ok {
+
+ val, er := cf([]string{key})
+ if er != nil {
+ err = er
+ return
+ }
+
+ v.Set(reflect.ValueOf(val))
+ return
+ }
+ }
+
+ switch kind {
+ case reflect.Interface:
+ // If interface would have been set on the struct before decoding,
+ // say to a struct value we would not get here but kind would be struct.
+ v.Set(reflect.ValueOf(key))
+ return
+ case reflect.Ptr:
+ newVal := reflect.New(v.Type().Elem())
+ if err = d.getMapKey(key, newVal.Elem(), namespace); err == nil {
+ v.Set(newVal)
+ }
+
+ case reflect.String:
+ v.SetString(key)
+
+ case reflect.Uint, reflect.Uint64:
+
+ u64, e := strconv.ParseUint(key, 10, 64)
+ if e != nil {
+ err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetUint(u64)
+
+ case reflect.Uint8:
+
+ u64, e := strconv.ParseUint(key, 10, 8)
+ if e != nil {
+ err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetUint(u64)
+
+ case reflect.Uint16:
+
+ u64, e := strconv.ParseUint(key, 10, 16)
+ if e != nil {
+ err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetUint(u64)
+
+ case reflect.Uint32:
+
+ u64, e := strconv.ParseUint(key, 10, 32)
+ if e != nil {
+ err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetUint(u64)
+
+ case reflect.Int, reflect.Int64:
+
+ i64, e := strconv.ParseInt(key, 10, 64)
+ if e != nil {
+ err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetInt(i64)
+
+ case reflect.Int8:
+
+ i64, e := strconv.ParseInt(key, 10, 8)
+ if e != nil {
+ err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetInt(i64)
+
+ case reflect.Int16:
+
+ i64, e := strconv.ParseInt(key, 10, 16)
+ if e != nil {
+ err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetInt(i64)
+
+ case reflect.Int32:
+
+ i64, e := strconv.ParseInt(key, 10, 32)
+ if e != nil {
+ err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetInt(i64)
+
+ case reflect.Float32:
+
+ f, e := strconv.ParseFloat(key, 32)
+ if e != nil {
+ err = fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetFloat(f)
+
+ case reflect.Float64:
+
+ f, e := strconv.ParseFloat(key, 64)
+ if e != nil {
+ err = fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetFloat(f)
+
+ case reflect.Bool:
+
+ b, e := parseBool(key)
+ if e != nil {
+ err = fmt.Errorf("Invalid Boolean Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ return
+ }
+
+ v.SetBool(b)
+
+ default:
+ err = fmt.Errorf("Unsupported Map Key '%s', Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
+ }
+
+ return
+}
diff --git a/vendor/github.com/go-playground/form/v4/doc.go b/vendor/github.com/go-playground/form/v4/doc.go
new file mode 100644
index 000000000..f553dac2c
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/doc.go
@@ -0,0 +1,275 @@
+/*
+Package form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values.
+
+
+It has the following features:
+
+ - Primitives types cause zero allocations.
+ - Supports map of almost all types.
+ - Supports both Numbered and Normal arrays eg. "Array[0]" and just "Array"
+ with multiple values passed.
+ - Slice honours the specified index. eg. if "Slice[2]" is the only Slice
+ value passed down, it will be put at index 2; if slice isn't big enough
+ it will be expanded.
+ - Array honours the specified index. eg. if "Array[2]" is the only Array
+ value passed down, it will be put at index 2; if array isn't big enough
+ a warning will be printed and value ignored.
+ - Only creates objects as necessary eg. if no `array` or `map` values are
+ passed down, the `array` and `map` are left as their default values in
+ the struct.
+ - Allows for Custom Type registration.
+ - Handles time.Time using RFC3339 time format by default,
+ but can easily be changed by registering a Custom Type, see below.
+ - Handles Encoding & Decoding of almost all Go types eg. can Decode into
+ struct, array, map, int... and Encode a struct, array, map, int...
+
+Common Questions
+
+Questions
+
+ Does it support encoding.TextUnmarshaler?
+ No because TextUnmarshaler only accepts []byte but posted values can have
+ multiple values, so is not suitable.
+
+ Mixing array/slice with array[idx]/slice[idx], in which order are they parsed?
+ array/slice then array[idx]/slice[idx]
+
+Supported Types
+
+out of the box supported types
+
+ - string
+ - bool
+ - int, int8, int16, int32, int64
+ - uint, uint8, uint16, uint32, uint64
+ - float32, float64
+ - struct and anonymous struct
+ - interface{}
+ - time.Time` - by default using RFC3339
+ - a `pointer` to one of the above types
+ - slice, array
+ - map
+ - `custom types` can override any of the above types
+ - many other types may be supported inherently (eg. bson.ObjectId is
+ type ObjectId string, which will get populated by the string type
+
+ **NOTE**: map, struct and slice nesting are ad infinitum.
+
+Usage
+
+symbols
+
+ - Use symbol `.` for separating fields/structs. (eg. `structfield.field`)
+ - Use `[index or key]` for access to index of a slice/array or key for map.
+ (eg. `arrayfield[0]`, `mapfield[keyvalue]`)
+
+html
+
+ <form method="POST">
+ <input type="text" name="Name" value="joeybloggs"/>
+ <input type="text" name="Age" value="3"/>
+ <input type="text" name="Gender" value="Male"/>
+ <input type="text" name="Address[0].Name" value="26 Here Blvd."/>
+ <input type="text" name="Address[0].Phone" value="9(999)999-9999"/>
+ <input type="text" name="Address[1].Name" value="26 There Blvd."/>
+ <input type="text" name="Address[1].Phone" value="1(111)111-1111"/>
+ <input type="text" name="active" value="true"/>
+ <input type="text" name="MapExample[key]" value="value"/>
+ <input type="text" name="NestedMap[key][key]" value="value"/>
+ <input type="text" name="NestedArray[0][0]" value="value"/>
+ <input type="submit"/>
+ </form>
+
+Example
+
+example decoding the above HTML
+
+ package main
+
+ import (
+ "fmt"
+ "log"
+ "net/url"
+
+ "github.com/go-playground/form/v4"
+ )
+
+ // Address contains address information
+ type Address struct {
+ Name string
+ Phone string
+ }
+
+ // User contains user information
+ type User struct {
+ Name string
+ Age uint8
+ Gender string
+ Address []Address
+ Active bool `form:"active"`
+ MapExample map[string]string
+ NestedMap map[string]map[string]string
+ NestedArray [][]string
+ }
+
+ // use a single instance of Decoder, it caches struct info
+ var decoder *form.Decoder
+
+ func main() {
+ decoder = form.NewDecoder()
+
+ // this simulates the results of http.Request's ParseForm() function
+ values := parseForm()
+
+ var user User
+
+ // must pass a pointer
+ err := decoder.Decode(&user, values)
+ if err != nil {
+ log.Panic(err)
+ }
+
+ fmt.Printf("%#v\n", user)
+ }
+
+ // this simulates the results of http.Request's ParseForm() function
+ func parseForm() url.Values {
+ return url.Values{
+ "Name": []string{"joeybloggs"},
+ "Age": []string{"3"},
+ "Gender": []string{"Male"},
+ "Address[0].Name": []string{"26 Here Blvd."},
+ "Address[0].Phone": []string{"9(999)999-9999"},
+ "Address[1].Name": []string{"26 There Blvd."},
+ "Address[1].Phone": []string{"1(111)111-1111"},
+ "active": []string{"true"},
+ "MapExample[key]": []string{"value"},
+ "NestedMap[key][key]": []string{"value"},
+ "NestedArray[0][0]": []string{"value"},
+ }
+ }
+
+example encoding
+
+ package main
+
+ import (
+ "fmt"
+ "log"
+
+ "github.com/go-playground/form/v4"
+ )
+
+ // Address contains address information
+ type Address struct {
+ Name string
+ Phone string
+ }
+
+ // User contains user information
+ type User struct {
+ Name string
+ Age uint8
+ Gender string
+ Address []Address
+ Active bool `form:"active"`
+ MapExample map[string]string
+ NestedMap map[string]map[string]string
+ NestedArray [][]string
+ }
+
+ // use a single instance of Encoder, it caches struct info
+ var encoder *form.Encoder
+
+ func main() {
+ encoder = form.NewEncoder()
+
+ user := User{
+ Name: "joeybloggs",
+ Age: 3,
+ Gender: "Male",
+ Address: []Address{
+ {Name: "26 Here Blvd.", Phone: "9(999)999-9999"},
+ {Name: "26 There Blvd.", Phone: "1(111)111-1111"},
+ },
+ Active: true,
+ MapExample: map[string]string{"key": "value"},
+ NestedMap: map[string]map[string]string{"key": {"key": "value"}},
+ NestedArray: [][]string{{"value"}},
+ }
+
+ // must pass a pointer
+ values, err := encoder.Encode(&user)
+ if err != nil {
+ log.Panic(err)
+ }
+
+ fmt.Printf("%#v\n", values)
+ }
+
+
+Registering Custom Types
+
+Decoder
+
+ decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
+ return time.Parse("2006-01-02", vals[0])
+ }, time.Time{})
+
+ 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.
+
+Encoder
+
+ encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
+ return []string{x.(time.Time).Format("2006-01-02")}, nil
+ }, time.Time{})
+
+
+Ignoring Fields
+
+you can tell form to ignore fields using `-` in the tag
+
+ type MyStruct struct {
+ Field string `form:"-"`
+ }
+
+Omitempty
+
+you can tell form to omit empty fields using `,omitempty` or `FieldName,omitempty` in the tag
+
+ type MyStruct struct {
+ Field string `form:",omitempty"`
+ Field2 string `form:"CustomFieldName,omitempty"`
+ }
+
+
+Notes
+
+To maximize compatibility with other systems the Encoder attempts
+to avoid using array indexes in url.Values if at all possible.
+
+ eg.
+
+ // A struct field of
+ Field []string{"1", "2", "3"}
+
+ // will be output a url.Value as
+ "Field": []string{"1", "2", "3"}
+
+ and not
+ "Field[0]": []string{"1"}
+ "Field[1]": []string{"2"}
+ "Field[2]": []string{"3"}
+
+ // however there are times where it is unavoidable, like with pointers
+ i := int(1)
+ Field []*string{nil, nil, &i}
+
+ // to avoid index 1 and 2 must use index
+ "Field[2]": []string{"1"}
+
+*/
+package form
diff --git a/vendor/github.com/go-playground/form/v4/encoder.go b/vendor/github.com/go-playground/form/v4/encoder.go
new file mode 100644
index 000000000..eea0bd9f3
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/encoder.go
@@ -0,0 +1,261 @@
+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
+ }
+}
diff --git a/vendor/github.com/go-playground/form/v4/form.go b/vendor/github.com/go-playground/form/v4/form.go
new file mode 100644
index 000000000..b1b4e7dc2
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/form.go
@@ -0,0 +1,49 @@
+package form
+
+import (
+ "reflect"
+ "time"
+)
+
+const (
+ blank = ""
+ ignore = "-"
+ fieldNS = "Field Namespace:"
+ errorText = " ERROR:"
+)
+
+var (
+ timeType = reflect.TypeOf(time.Time{})
+)
+
+// Mode specifies which mode the form decoder is to run
+type Mode uint8
+
+const (
+
+ // ModeImplicit tries to parse values for all
+ // fields that do not have an ignore '-' tag
+ ModeImplicit Mode = iota
+
+ // ModeExplicit only parses values for field with a field tag
+ // and that tag is not the ignore '-' tag
+ ModeExplicit
+)
+
+// AnonymousMode specifies how data should be rolled up
+// or separated from anonymous structs
+type AnonymousMode uint8
+
+const (
+ // AnonymousEmbed embeds anonymous data when encoding
+ // eg. type A struct { Field string }
+ // type B struct { A, Field string }
+ // encode results: url.Values{"Field":[]string{"B FieldVal", "A FieldVal"}}
+ AnonymousEmbed AnonymousMode = iota
+
+ // AnonymousSeparate does not embed anonymous data when encoding
+ // eg. type A struct { Field string }
+ // type B struct { A, Field string }
+ // encode results: url.Values{"Field":[]string{"B FieldVal"}, "A.Field":[]string{"A FieldVal"}}
+ AnonymousSeparate
+)
diff --git a/vendor/github.com/go-playground/form/v4/form_decoder.go b/vendor/github.com/go-playground/form/v4/form_decoder.go
new file mode 100644
index 000000000..ac131ea8b
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/form_decoder.go
@@ -0,0 +1,187 @@
+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
+ namespacePrefix string
+ namespaceSuffix string
+}
+
+// NewDecoder creates a new decoder instance with sane defaults
+func NewDecoder() *Decoder {
+
+ d := &Decoder{
+ tagName: "form",
+ mode: ModeImplicit,
+ structCache: newStructCacheMap(),
+ maxArraySize: 10000,
+ namespacePrefix: ".",
+ }
+
+ 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
+}
+
+// SetNamespacePrefix sets a struct namespace prefix.
+func (d *Decoder) SetNamespacePrefix(namespacePrefix string) {
+ d.namespacePrefix = namespacePrefix
+}
+
+// SetNamespaceSuffix sets a struct namespace suffix.
+func (d *Decoder) SetNamespaceSuffix(namespaceSuffix string) {
+ d.namespaceSuffix = namespaceSuffix
+}
+
+// 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
+}
diff --git a/vendor/github.com/go-playground/form/v4/form_encoder.go b/vendor/github.com/go-playground/form/v4/form_encoder.go
new file mode 100644
index 000000000..48960c9a1
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/form_encoder.go
@@ -0,0 +1,157 @@
+package form
+
+import (
+ "bytes"
+ "net/url"
+ "reflect"
+ "strings"
+ "sync"
+)
+
+// EncodeCustomTypeFunc allows for registering/overriding types to be parsed.
+type EncodeCustomTypeFunc func(x interface{}) ([]string, error)
+
+// EncodeErrors is a map of errors encountered during form encoding
+type EncodeErrors map[string]error
+
+func (e EncodeErrors) Error() string {
+ buff := bytes.NewBufferString(blank)
+
+ for k, err := range e {
+ buff.WriteString(fieldNS)
+ buff.WriteString(k)
+ buff.WriteString(errorText)
+ buff.WriteString(err.Error())
+ buff.WriteString("\n")
+ }
+
+ return strings.TrimSpace(buff.String())
+}
+
+// An InvalidEncodeError describes an invalid argument passed to Encode.
+type InvalidEncodeError struct {
+ Type reflect.Type
+}
+
+func (e *InvalidEncodeError) Error() string {
+
+ if e.Type == nil {
+ return "form: Encode(nil)"
+ }
+
+ return "form: Encode(nil " + e.Type.String() + ")"
+}
+
+// Encoder is the main encode instance
+type Encoder struct {
+ tagName string
+ structCache *structCacheMap
+ customTypeFuncs map[reflect.Type]EncodeCustomTypeFunc
+ dataPool *sync.Pool
+ mode Mode
+ embedAnonymous bool
+ namespacePrefix string
+ namespaceSuffix string
+}
+
+// NewEncoder creates a new encoder instance with sane defaults
+func NewEncoder() *Encoder {
+
+ e := &Encoder{
+ tagName: "form",
+ mode: ModeImplicit,
+ structCache: newStructCacheMap(),
+ embedAnonymous: true,
+ namespacePrefix: ".",
+ }
+
+ e.dataPool = &sync.Pool{New: func() interface{} {
+ return &encoder{
+ e: e,
+ namespace: make([]byte, 0, 64),
+ }
+ }}
+
+ return e
+}
+
+// SetTagName sets the given tag name to be used by the encoder.
+// Default is "form"
+func (e *Encoder) SetTagName(tagName string) {
+ e.tagName = tagName
+}
+
+// SetMode sets the mode the encoder should run
+// Default is ModeImplicit
+func (e *Encoder) SetMode(mode Mode) {
+ e.mode = mode
+}
+
+// SetNamespacePrefix sets a struct namespace prefix.
+func (e *Encoder) SetNamespacePrefix(namespacePrefix string) {
+ e.namespacePrefix = namespacePrefix
+}
+
+// SetNamespaceSuffix sets a struct namespace suffix.
+func (e *Encoder) SetNamespaceSuffix(namespaceSuffix string) {
+ e.namespaceSuffix = namespaceSuffix
+}
+
+// SetAnonymousMode sets the mode the encoder should run
+// Default is AnonymousEmbed
+func (e *Encoder) SetAnonymousMode(mode AnonymousMode) {
+ e.embedAnonymous = mode == AnonymousEmbed
+}
+
+// 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 (e *Encoder) RegisterTagNameFunc(fn TagNameFunc) {
+ e.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
+func (e *Encoder) RegisterCustomTypeFunc(fn EncodeCustomTypeFunc, types ...interface{}) {
+
+ if e.customTypeFuncs == nil {
+ e.customTypeFuncs = map[reflect.Type]EncodeCustomTypeFunc{}
+ }
+
+ for _, t := range types {
+ e.customTypeFuncs[reflect.TypeOf(t)] = fn
+ }
+}
+
+// Encode encodes the given values and sets the corresponding struct values
+func (e *Encoder) Encode(v interface{}) (values url.Values, err error) {
+
+ val, kind := ExtractType(reflect.ValueOf(v))
+
+ if kind == reflect.Ptr || kind == reflect.Interface || kind == reflect.Invalid {
+ return nil, &InvalidEncodeError{reflect.TypeOf(v)}
+ }
+
+ enc := e.dataPool.Get().(*encoder)
+ enc.values = make(url.Values)
+
+ if kind == reflect.Struct && val.Type() != timeType {
+ enc.traverseStruct(val, enc.namespace[0:0], -1)
+ } else {
+ enc.setFieldByType(val, enc.namespace[0:0], -1, false)
+ }
+
+ if len(enc.errs) > 0 {
+ err = enc.errs
+ enc.errs = nil
+ }
+
+ values = enc.values
+
+ e.dataPool.Put(enc)
+
+ return
+}
diff --git a/vendor/github.com/go-playground/form/v4/logo.jpg b/vendor/github.com/go-playground/form/v4/logo.jpg
new file mode 100644
index 000000000..2ef34f87e
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/logo.jpg
Binary files differ
diff --git a/vendor/github.com/go-playground/form/v4/util.go b/vendor/github.com/go-playground/form/v4/util.go
new file mode 100644
index 000000000..02c86af72
--- /dev/null
+++ b/vendor/github.com/go-playground/form/v4/util.go
@@ -0,0 +1,62 @@
+package form
+
+import (
+ "reflect"
+ "strconv"
+)
+
+// ExtractType gets the actual underlying type of field value.
+// it is exposed for use within you Custom Functions
+func ExtractType(current reflect.Value) (reflect.Value, reflect.Kind) {
+
+ switch current.Kind() {
+ case reflect.Ptr:
+
+ if current.IsNil() {
+ return current, reflect.Ptr
+ }
+
+ return ExtractType(current.Elem())
+
+ case reflect.Interface:
+
+ if current.IsNil() {
+ return current, reflect.Interface
+ }
+
+ return ExtractType(current.Elem())
+
+ default:
+ return current, current.Kind()
+ }
+}
+
+func parseBool(str string) (bool, error) {
+
+ switch str {
+ case "1", "t", "T", "true", "TRUE", "True", "on", "yes", "ok":
+ return true, nil
+ case "", "0", "f", "F", "false", "FALSE", "False", "off", "no":
+ return false, nil
+ }
+
+ // strconv.NumError mimicing exactly the strconv.ParseBool(..) error and type
+ // to ensure compatibility with std library and beyond.
+ return false, &strconv.NumError{Func: "ParseBool", Num: str, Err: strconv.ErrSyntax}
+}
+
+// hasValue determines if a reflect.Value is it's default value
+func hasValue(field reflect.Value) bool {
+ switch field.Kind() {
+ case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
+ return !field.IsNil()
+ default:
+ if !field.IsValid() {
+ return false
+ }
+ if !field.Type().Comparable() {
+ return true
+ }
+ return field.Interface() != reflect.Zero(field.Type()).Interface()
+ }
+}