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  )