github.com/abemedia/go-don@v0.2.2-0.20240329015135-be88e32bb73b/encoding/text/decode.go (about) 1 package text 2 3 import ( 4 "bytes" 5 "encoding" 6 "reflect" 7 "strconv" 8 "sync" 9 10 "github.com/abemedia/go-don" 11 "github.com/abemedia/go-don/internal/byteconv" 12 "github.com/valyala/fasthttp" 13 ) 14 15 //nolint:cyclop 16 func decode(ctx *fasthttp.RequestCtx, v any) error { 17 b := bytes.TrimSpace(ctx.Request.Body()) 18 if len(b) == 0 { 19 return nil 20 } 21 22 var err error 23 24 switch v := v.(type) { 25 case *string: 26 *v = byteconv.Btoa(b) 27 case *[]byte: 28 *v = b 29 case *int: 30 *v, err = strconv.Atoi(byteconv.Btoa(b)) 31 case *int8: 32 return decodeInt(b, v, 8) 33 case *int16: 34 return decodeInt(b, v, 16) 35 case *int32: 36 return decodeInt(b, v, 32) 37 case *int64: 38 return decodeInt(b, v, 64) 39 case *uint: 40 return decodeUint(b, v, 0) 41 case *uint8: 42 return decodeUint(b, v, 8) 43 case *uint16: 44 return decodeUint(b, v, 16) 45 case *uint32: 46 return decodeUint(b, v, 32) 47 case *uint64: 48 return decodeUint(b, v, 64) 49 case *float32: 50 return decodeFloat(b, v, 32) 51 case *float64: 52 return decodeFloat(b, v, 64) 53 case *bool: 54 *v, err = strconv.ParseBool(byteconv.Btoa(b)) 55 default: 56 return unmarshal(b, v) 57 } 58 59 return err 60 } 61 62 func decodeInt[T int | int8 | int16 | int32 | int64](b []byte, v *T, bits int) error { 63 d, err := strconv.ParseInt(byteconv.Btoa(b), 10, bits) 64 *v = T(d) 65 return err 66 } 67 68 func decodeUint[T uint | uint8 | uint16 | uint32 | uint64](b []byte, v *T, bits int) error { 69 d, err := strconv.ParseUint(byteconv.Btoa(b), 10, bits) 70 *v = T(d) 71 return err 72 } 73 74 func decodeFloat[T float32 | float64](b []byte, v *T, bits int) error { 75 d, err := strconv.ParseFloat(byteconv.Btoa(b), bits) 76 *v = T(d) 77 return err 78 } 79 80 func unmarshal(b []byte, v any) error { 81 val := reflect.ValueOf(v) 82 typ := val.Type() 83 if dec, ok := unmarshalers.Load(typ); ok { 84 return dec.(func([]byte, reflect.Value) error)(b, val) //nolint:forcetypeassert 85 } 86 dec, err := newUnmarshaler(typ) 87 if err != nil { 88 return err 89 } 90 unmarshalers.Store(typ, dec) 91 return dec(b, val) 92 } 93 94 func newUnmarshaler(typ reflect.Type) (func([]byte, reflect.Value) error, error) { 95 if typ.Implements(unmarshalerType) { 96 isPtr := typ.Kind() == reflect.Pointer 97 typ = typ.Elem() 98 return func(b []byte, v reflect.Value) error { 99 if isPtr && v.IsNil() { 100 v.Set(reflect.New(typ)) 101 } 102 return v.Interface().(encoding.TextUnmarshaler).UnmarshalText(b) //nolint:forcetypeassert 103 }, nil 104 } 105 106 if typ.Kind() == reflect.Pointer { 107 typ = typ.Elem() 108 dec, err := newUnmarshaler(typ) 109 if err != nil { 110 return nil, err 111 } 112 return func(b []byte, v reflect.Value) error { 113 if v.IsNil() { 114 v.Set(reflect.New(typ)) 115 } 116 return dec(b, v.Elem()) 117 }, nil 118 } 119 120 return nil, don.ErrUnsupportedMediaType 121 } 122 123 var ( 124 unmarshalers sync.Map 125 unmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem() 126 )