// Copyright 2012 Gary Burd // // Licensed under the Apache License, Version 2.0 (the "License"): you may // not use this file except in compliance with the License. You may obtain // a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the // License for the specific language governing permissions and limitations // under the License. package redis import ( "errors" "fmt" "reflect" "strconv" "strings" "sync" pkgerr "github.com/pkg/errors" ) func ensureLen(d reflect.Value, n int) { if n > d.Cap() { d.Set(reflect.MakeSlice(d.Type(), n, n)) } else { d.SetLen(n) } } func cannotConvert(d reflect.Value, s interface{}) error { var sname string switch s.(type) { case string: sname = "Redis simple string" case Error: sname = "Redis error" case int64: sname = "Redis integer" case []byte: sname = "Redis bulk string" case []interface{}: sname = "Redis array" default: sname = reflect.TypeOf(s).String() } return pkgerr.Errorf("cannot convert from %s to %s", sname, d.Type()) } func convertAssignBulkString(d reflect.Value, s []byte) (err error) { switch d.Type().Kind() { case reflect.Float32, reflect.Float64: var x float64 x, err = strconv.ParseFloat(string(s), d.Type().Bits()) d.SetFloat(x) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var x int64 x, err = strconv.ParseInt(string(s), 10, d.Type().Bits()) d.SetInt(x) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: var x uint64 x, err = strconv.ParseUint(string(s), 10, d.Type().Bits()) d.SetUint(x) case reflect.Bool: var x bool x, err = strconv.ParseBool(string(s)) d.SetBool(x) case reflect.String: d.SetString(string(s)) case reflect.Slice: if d.Type().Elem().Kind() != reflect.Uint8 { err = cannotConvert(d, s) } else { d.SetBytes(s) } default: err = cannotConvert(d, s) } err = pkgerr.WithStack(err) return } func convertAssignInt(d reflect.Value, s int64) (err error) { switch d.Type().Kind() { case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: d.SetInt(s) if d.Int() != s { err = strconv.ErrRange d.SetInt(0) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if s < 0 { err = strconv.ErrRange } else { x := uint64(s) d.SetUint(x) if d.Uint() != x { err = strconv.ErrRange d.SetUint(0) } } case reflect.Bool: d.SetBool(s != 0) default: err = cannotConvert(d, s) } err = pkgerr.WithStack(err) return } func convertAssignValue(d reflect.Value, s interface{}) (err error) { switch s := s.(type) { case []byte: err = convertAssignBulkString(d, s) case int64: err = convertAssignInt(d, s) default: err = cannotConvert(d, s) } return err } func convertAssignArray(d reflect.Value, s []interface{}) error { if d.Type().Kind() != reflect.Slice { return cannotConvert(d, s) } ensureLen(d, len(s)) for i := 0; i < len(s); i++ { if err := convertAssignValue(d.Index(i), s[i]); err != nil { return err } } return nil } func convertAssign(d interface{}, s interface{}) (err error) { // Handle the most common destination types using type switches and // fall back to reflection for all other types. switch s := s.(type) { case nil: // ingore case []byte: switch d := d.(type) { case *string: *d = string(s) case *int: *d, err = strconv.Atoi(string(s)) case *bool: *d, err = strconv.ParseBool(string(s)) case *[]byte: *d = s case *interface{}: *d = s case nil: // skip value default: if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr { err = cannotConvert(d, s) } else { err = convertAssignBulkString(d.Elem(), s) } } case int64: switch d := d.(type) { case *int: x := int(s) if int64(x) != s { err = strconv.ErrRange x = 0 } *d = x case *bool: *d = s != 0 case *interface{}: *d = s case nil: // skip value default: if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr { err = cannotConvert(d, s) } else { err = convertAssignInt(d.Elem(), s) } } case string: switch d := d.(type) { case *string: *d = string(s) default: err = cannotConvert(reflect.ValueOf(d), s) } case []interface{}: switch d := d.(type) { case *[]interface{}: *d = s case *interface{}: *d = s case nil: // skip value default: if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr { err = cannotConvert(d, s) } else { err = convertAssignArray(d.Elem(), s) } } case Error: err = s default: err = cannotConvert(reflect.ValueOf(d), s) } err = pkgerr.WithStack(err) return } // Scan copies from src to the values pointed at by dest. // // The values pointed at by dest must be an integer, float, boolean, string, // []byte, interface{} or slices of these types. Scan uses the standard strconv // package to convert bulk strings to numeric and boolean types. // // If a dest value is nil, then the corresponding src value is skipped. // // If a src element is nil, then the corresponding dest value is not modified. // // To enable easy use of Scan in a loop, Scan returns the slice of src // following the copied values. func Scan(src []interface{}, dest ...interface{}) ([]interface{}, error) { if len(src) < len(dest) { return nil, pkgerr.New("redigo.Scan: array short") } var err error for i, d := range dest { err = convertAssign(d, src[i]) if err != nil { err = fmt.Errorf("redigo.Scan: cannot assign to dest %d: %v", i, err) break } } return src[len(dest):], err } type fieldSpec struct { name string index []int omitEmpty bool } type structSpec struct { m map[string]*fieldSpec l []*fieldSpec } func (ss *structSpec) fieldSpec(name []byte) *fieldSpec { return ss.m[string(name)] } func compileStructSpec(t reflect.Type, depth map[string]int, index []int, ss *structSpec) { for i := 0; i < t.NumField(); i++ { f := t.Field(i) switch { case f.PkgPath != "" && !f.Anonymous: // Ignore unexported fields. case f.Anonymous: // TODO: Handle pointers. Requires change to decoder and // protection against infinite recursion. if f.Type.Kind() == reflect.Struct { compileStructSpec(f.Type, depth, append(index, i), ss) } default: fs := &fieldSpec{name: f.Name} tag := f.Tag.Get("redis") p := strings.Split(tag, ",") if len(p) > 0 { if p[0] == "-" { continue } if len(p[0]) > 0 { fs.name = p[0] } for _, s := range p[1:] { switch s { case "omitempty": fs.omitEmpty = true default: panic(fmt.Errorf("redigo: unknown field tag %s for type %s", s, t.Name())) } } } d, found := depth[fs.name] if !found { d = 1 << 30 } switch { case len(index) == d: // At same depth, remove from result. delete(ss.m, fs.name) j := 0 for i1 := 0; i1 < len(ss.l); i1++ { if fs.name != ss.l[i1].name { ss.l[j] = ss.l[i1] j++ } } ss.l = ss.l[:j] case len(index) < d: fs.index = make([]int, len(index)+1) copy(fs.index, index) fs.index[len(index)] = i depth[fs.name] = len(index) ss.m[fs.name] = fs ss.l = append(ss.l, fs) } } } } var ( structSpecMutex sync.RWMutex structSpecCache = make(map[reflect.Type]*structSpec) ) func structSpecForType(t reflect.Type) *structSpec { structSpecMutex.RLock() ss, found := structSpecCache[t] structSpecMutex.RUnlock() if found { return ss } structSpecMutex.Lock() defer structSpecMutex.Unlock() ss, found = structSpecCache[t] if found { return ss } ss = &structSpec{m: make(map[string]*fieldSpec)} compileStructSpec(t, make(map[string]int), nil, ss) structSpecCache[t] = ss return ss } var errScanStructValue = errors.New("redigo.ScanStruct: value must be non-nil pointer to a struct") // ScanStruct scans alternating names and values from src to a struct. The // HGETALL and CONFIG GET commands return replies in this format. // // ScanStruct uses exported field names to match values in the response. Use // 'redis' field tag to override the name: // // Field int `redis:"myName"` // // Fields with the tag redis:"-" are ignored. // // Integer, float, boolean, string and []byte fields are supported. Scan uses the // standard strconv package to convert bulk string values to numeric and // boolean types. // // If a src element is nil, then the corresponding field is not modified. func ScanStruct(src []interface{}, dest interface{}) error { d := reflect.ValueOf(dest) if d.Kind() != reflect.Ptr || d.IsNil() { return pkgerr.WithStack(errScanStructValue) } d = d.Elem() if d.Kind() != reflect.Struct { return pkgerr.WithStack(errScanStructValue) } ss := structSpecForType(d.Type()) if len(src)%2 != 0 { return pkgerr.New("redigo.ScanStruct: number of values not a multiple of 2") } for i := 0; i < len(src); i += 2 { s := src[i+1] if s == nil { continue } name, ok := src[i].([]byte) if !ok { return pkgerr.Errorf("redigo.ScanStruct: key %d not a bulk string value", i) } fs := ss.fieldSpec(name) if fs == nil { continue } if err := convertAssignValue(d.FieldByIndex(fs.index), s); err != nil { return pkgerr.Errorf("redigo.ScanStruct: cannot assign field %s: %v", fs.name, err) } } return nil } var ( errScanSliceValue = errors.New("redigo.ScanSlice: dest must be non-nil pointer to a struct") ) // ScanSlice scans src to the slice pointed to by dest. The elements the dest // slice must be integer, float, boolean, string, struct or pointer to struct // values. // // Struct fields must be integer, float, boolean or string values. All struct // fields are used unless a subset is specified using fieldNames. func ScanSlice(src []interface{}, dest interface{}, fieldNames ...string) error { d := reflect.ValueOf(dest) if d.Kind() != reflect.Ptr || d.IsNil() { return pkgerr.WithStack(errScanSliceValue) } d = d.Elem() if d.Kind() != reflect.Slice { return pkgerr.WithStack(errScanSliceValue) } isPtr := false t := d.Type().Elem() if t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct { isPtr = true t = t.Elem() } if t.Kind() != reflect.Struct { ensureLen(d, len(src)) for i, s := range src { if s == nil { continue } if err := convertAssignValue(d.Index(i), s); err != nil { return pkgerr.Errorf("redigo.ScanSlice: cannot assign element %d: %v", i, err) } } return nil } ss := structSpecForType(t) fss := ss.l if len(fieldNames) > 0 { fss = make([]*fieldSpec, len(fieldNames)) for i, name := range fieldNames { fss[i] = ss.m[name] if fss[i] == nil { return pkgerr.Errorf("redigo.ScanSlice: ScanSlice bad field name %s", name) } } } if len(fss) == 0 { return pkgerr.New("redigo.ScanSlice: no struct fields") } n := len(src) / len(fss) if n*len(fss) != len(src) { return pkgerr.New("redigo.ScanSlice: length not a multiple of struct field count") } ensureLen(d, n) for i := 0; i < n; i++ { d1 := d.Index(i) if isPtr { if d1.IsNil() { d1.Set(reflect.New(t)) } d1 = d1.Elem() } for j, fs := range fss { s := src[i*len(fss)+j] if s == nil { continue } if err := convertAssignValue(d1.FieldByIndex(fs.index), s); err != nil { return pkgerr.Errorf("redigo.ScanSlice: cannot assign element %d to field %s: %v", i*len(fss)+j, fs.name, err) } } } return nil } // Args is a helper for constructing command arguments from structured values. type Args []interface{} // Add returns the result of appending value to args. func (args Args) Add(value ...interface{}) Args { return append(args, value...) } // AddFlat returns the result of appending the flattened value of v to args. // // Maps are flattened by appending the alternating keys and map values to args. // // Slices are flattened by appending the slice elements to args. // // Structs are flattened by appending the alternating names and values of // exported fields to args. If v is a nil struct pointer, then nothing is // appended. The 'redis' field tag overrides struct field names. See ScanStruct // for more information on the use of the 'redis' field tag. // // Other types are appended to args as is. func (args Args) AddFlat(v interface{}) Args { rv := reflect.ValueOf(v) switch rv.Kind() { case reflect.Struct: args = flattenStruct(args, rv) case reflect.Slice: for i := 0; i < rv.Len(); i++ { args = append(args, rv.Index(i).Interface()) } case reflect.Map: for _, k := range rv.MapKeys() { args = append(args, k.Interface(), rv.MapIndex(k).Interface()) } case reflect.Ptr: if rv.Type().Elem().Kind() == reflect.Struct { if !rv.IsNil() { args = flattenStruct(args, rv.Elem()) } } else { args = append(args, v) } default: args = append(args, v) } return args } func flattenStruct(args Args, v reflect.Value) Args { ss := structSpecForType(v.Type()) for _, fs := range ss.l { fv := v.FieldByIndex(fs.index) if fs.omitEmpty { var empty = false switch fv.Kind() { case reflect.Array, reflect.Map, reflect.Slice, reflect.String: empty = fv.Len() == 0 case reflect.Bool: empty = !fv.Bool() case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: empty = fv.Int() == 0 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: empty = fv.Uint() == 0 case reflect.Float32, reflect.Float64: empty = fv.Float() == 0 case reflect.Interface, reflect.Ptr: empty = fv.IsNil() } if empty { continue } } args = append(args, fs.name, fv.Interface()) } return args }