package proto import ( "encoding" "fmt" "reflect" "time" "github.com/go-redis/redis/v8/internal/util" ) // Scan parses bytes `b` to `v` with appropriate type. func Scan(b []byte, v interface{}) error { switch v := v.(type) { case nil: return fmt.Errorf("redis: Scan(nil)") case *string: *v = util.BytesToString(b) return nil case *[]byte: *v = b return nil case *int: var err error *v, err = util.Atoi(b) return err case *int8: n, err := util.ParseInt(b, 10, 8) if err != nil { return err } *v = int8(n) return nil case *int16: n, err := util.ParseInt(b, 10, 16) if err != nil { return err } *v = int16(n) return nil case *int32: n, err := util.ParseInt(b, 10, 32) if err != nil { return err } *v = int32(n) return nil case *int64: n, err := util.ParseInt(b, 10, 64) if err != nil { return err } *v = n return nil case *uint: n, err := util.ParseUint(b, 10, 64) if err != nil { return err } *v = uint(n) return nil case *uint8: n, err := util.ParseUint(b, 10, 8) if err != nil { return err } *v = uint8(n) return nil case *uint16: n, err := util.ParseUint(b, 10, 16) if err != nil { return err } *v = uint16(n) return nil case *uint32: n, err := util.ParseUint(b, 10, 32) if err != nil { return err } *v = uint32(n) return nil case *uint64: n, err := util.ParseUint(b, 10, 64) if err != nil { return err } *v = n return nil case *float32: n, err := util.ParseFloat(b, 32) if err != nil { return err } *v = float32(n) return err case *float64: var err error *v, err = util.ParseFloat(b, 64) return err case *bool: *v = len(b) == 1 && b[0] == '1' return nil case *time.Time: var err error *v, err = time.Parse(time.RFC3339Nano, util.BytesToString(b)) return err case encoding.BinaryUnmarshaler: return v.UnmarshalBinary(b) default: return fmt.Errorf( "redis: can't unmarshal %T (consider implementing BinaryUnmarshaler)", v) } } func ScanSlice(data []string, slice interface{}) error { v := reflect.ValueOf(slice) if !v.IsValid() { return fmt.Errorf("redis: ScanSlice(nil)") } if v.Kind() != reflect.Ptr { return fmt.Errorf("redis: ScanSlice(non-pointer %T)", slice) } v = v.Elem() if v.Kind() != reflect.Slice { return fmt.Errorf("redis: ScanSlice(non-slice %T)", slice) } next := makeSliceNextElemFunc(v) for i, s := range data { elem := next() if err := Scan([]byte(s), elem.Addr().Interface()); err != nil { err = fmt.Errorf("redis: ScanSlice index=%d value=%q failed: %w", i, s, err) return err } } return nil } func makeSliceNextElemFunc(v reflect.Value) func() reflect.Value { elemType := v.Type().Elem() if elemType.Kind() == reflect.Ptr { elemType = elemType.Elem() return func() reflect.Value { if v.Len() < v.Cap() { v.Set(v.Slice(0, v.Len()+1)) elem := v.Index(v.Len() - 1) if elem.IsNil() { elem.Set(reflect.New(elemType)) } return elem.Elem() } elem := reflect.New(elemType) v.Set(reflect.Append(v, elem)) return elem.Elem() } } zero := reflect.Zero(elemType) return func() reflect.Value { if v.Len() < v.Cap() { v.Set(v.Slice(0, v.Len()+1)) return v.Index(v.Len() - 1) } v.Set(reflect.Append(v, zero)) return v.Index(v.Len() - 1) } }