mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 15:32:24 -05:00 
			
		
		
		
	[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>
This commit is contained in:
		
					parent
					
						
							
								24f6a447f3
							
						
					
				
			
			
				commit
				
					
						f518f649f8
					
				
			
		
					 21 changed files with 2399 additions and 2 deletions
				
			
		
							
								
								
									
										28
									
								
								vendor/github.com/go-playground/form/v4/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								vendor/github.com/go-playground/form/v4/.gitignore
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||
							
								
								
									
										26
									
								
								vendor/github.com/go-playground/form/v4/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								vendor/github.com/go-playground/form/v4/.travis.yml
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||
							
								
								
									
										21
									
								
								vendor/github.com/go-playground/form/v4/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								vendor/github.com/go-playground/form/v4/LICENSE
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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. | ||||
							
								
								
									
										333
									
								
								vendor/github.com/go-playground/form/v4/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										333
									
								
								vendor/github.com/go-playground/form/v4/README.md
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -0,0 +1,333 @@ | |||
| Package form | ||||
| ============ | ||||
| <img align="right" src="https://raw.githubusercontent.com/go-playground/form/master/logo.jpg"> | ||||
| [](https://github.com/go-playground/form/actions/workflows/workflow.yml) | ||||
| [](https://coveralls.io/github/go-playground/form?branch=master) | ||||
| [](https://goreportcard.com/report/github.com/go-playground/form) | ||||
| [](https://godoc.org/github.com/go-playground/form) | ||||
|  | ||||
| [](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. | ||||
							
								
								
									
										133
									
								
								vendor/github.com/go-playground/form/v4/cache.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								vendor/github.com/go-playground/form/v4/cache.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| } | ||||
							
								
								
									
										748
									
								
								vendor/github.com/go-playground/form/v4/decoder.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										748
									
								
								vendor/github.com/go-playground/form/v4/decoder.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| } | ||||
							
								
								
									
										275
									
								
								vendor/github.com/go-playground/form/v4/doc.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								vendor/github.com/go-playground/form/v4/doc.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||
							
								
								
									
										261
									
								
								vendor/github.com/go-playground/form/v4/encoder.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								vendor/github.com/go-playground/form/v4/encoder.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										49
									
								
								vendor/github.com/go-playground/form/v4/form.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								vendor/github.com/go-playground/form/v4/form.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| ) | ||||
							
								
								
									
										187
									
								
								vendor/github.com/go-playground/form/v4/form_decoder.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								vendor/github.com/go-playground/form/v4/form_decoder.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| } | ||||
							
								
								
									
										157
									
								
								vendor/github.com/go-playground/form/v4/form_encoder.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								vendor/github.com/go-playground/form/v4/form_encoder.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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 | ||||
| } | ||||
							
								
								
									
										
											BIN
										
									
								
								vendor/github.com/go-playground/form/v4/logo.jpg
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								vendor/github.com/go-playground/form/v4/logo.jpg
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 20 KiB | 
							
								
								
									
										62
									
								
								vendor/github.com/go-playground/form/v4/util.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								vendor/github.com/go-playground/form/v4/util.go
									
										
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							|  | @ -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() | ||||
| 	} | ||||
| } | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue