summaryrefslogtreecommitdiff
path: root/vendor/github.com/go-playground/form/v4/decoder.go
diff options
context:
space:
mode:
authorLibravatar zowhoey <11893985+zowhoey@users.noreply.github.com>2023-03-06 09:30:19 +0000
committerLibravatar GitHub <noreply@github.com>2023-03-06 10:30:19 +0100
commitf518f649f800e52d32fe53580c528ffa042f7154 (patch)
treec14bb8b513008d1c1e7bab0908cfc99fe18f1507 /vendor/github.com/go-playground/form/v4/decoder.go
parent[chore]: Bump github.com/jackc/pgx/v4 from 4.17.2 to 4.18.1 (#1595) (diff)
downloadgotosocial-f518f649f800e52d32fe53580c528ffa042f7154.tar.xz
[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>
Diffstat (limited to 'vendor/github.com/go-playground/form/v4/decoder.go')
-rw-r--r--vendor/github.com/go-playground/form/v4/decoder.go748
1 files changed, 748 insertions, 0 deletions
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
+}