...

Source file src/github.com/redis/go-redis/v9/internal/hscan/structmap.go

Documentation: github.com/redis/go-redis/v9/internal/hscan

     1  package hscan
     2  
     3  import (
     4  	"encoding"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  	"sync"
     9  
    10  	"github.com/redis/go-redis/v9/internal/util"
    11  )
    12  
    13  // structMap contains the map of struct fields for target structs
    14  // indexed by the struct type.
    15  type structMap struct {
    16  	m sync.Map
    17  }
    18  
    19  func newStructMap() *structMap {
    20  	return new(structMap)
    21  }
    22  
    23  func (s *structMap) get(t reflect.Type) *structSpec {
    24  	if v, ok := s.m.Load(t); ok {
    25  		return v.(*structSpec)
    26  	}
    27  
    28  	spec := newStructSpec(t, "redis")
    29  	s.m.Store(t, spec)
    30  	return spec
    31  }
    32  
    33  //------------------------------------------------------------------------------
    34  
    35  // structSpec contains the list of all fields in a target struct.
    36  type structSpec struct {
    37  	m map[string]*structField
    38  }
    39  
    40  func (s *structSpec) set(tag string, sf *structField) {
    41  	s.m[tag] = sf
    42  }
    43  
    44  func newStructSpec(t reflect.Type, fieldTag string) *structSpec {
    45  	numField := t.NumField()
    46  	out := &structSpec{
    47  		m: make(map[string]*structField, numField),
    48  	}
    49  
    50  	for i := 0; i < numField; i++ {
    51  		f := t.Field(i)
    52  
    53  		tag := f.Tag.Get(fieldTag)
    54  		if tag == "" || tag == "-" {
    55  			continue
    56  		}
    57  
    58  		tag = strings.Split(tag, ",")[0]
    59  		if tag == "" {
    60  			continue
    61  		}
    62  
    63  		// Use the built-in decoder.
    64  		kind := f.Type.Kind()
    65  		if kind == reflect.Pointer {
    66  			kind = f.Type.Elem().Kind()
    67  		}
    68  		out.set(tag, &structField{index: i, fn: decoders[kind]})
    69  	}
    70  
    71  	return out
    72  }
    73  
    74  //------------------------------------------------------------------------------
    75  
    76  // structField represents a single field in a target struct.
    77  type structField struct {
    78  	index int
    79  	fn    decoderFunc
    80  }
    81  
    82  //------------------------------------------------------------------------------
    83  
    84  type StructValue struct {
    85  	spec  *structSpec
    86  	value reflect.Value
    87  }
    88  
    89  func (s StructValue) Scan(key string, value string) error {
    90  	field, ok := s.spec.m[key]
    91  	if !ok {
    92  		return nil
    93  	}
    94  
    95  	v := s.value.Field(field.index)
    96  	isPtr := v.Kind() == reflect.Ptr
    97  
    98  	if isPtr && v.IsNil() {
    99  		v.Set(reflect.New(v.Type().Elem()))
   100  	}
   101  	if !isPtr && v.Type().Name() != "" && v.CanAddr() {
   102  		v = v.Addr()
   103  		isPtr = true
   104  	}
   105  
   106  	if isPtr && v.Type().NumMethod() > 0 && v.CanInterface() {
   107  		switch scan := v.Interface().(type) {
   108  		case Scanner:
   109  			return scan.ScanRedis(value)
   110  		case encoding.TextUnmarshaler:
   111  			return scan.UnmarshalText(util.StringToBytes(value))
   112  		}
   113  	}
   114  
   115  	if isPtr {
   116  		v = v.Elem()
   117  	}
   118  
   119  	if err := field.fn(v, value); err != nil {
   120  		t := s.value.Type()
   121  		return fmt.Errorf("cannot scan redis.result %s into struct field %s.%s of type %s, error-%s",
   122  			value, t.Name(), t.Field(field.index).Name, t.Field(field.index).Type, err.Error())
   123  	}
   124  	return nil
   125  }
   126  

View as plain text