struct.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. // Copyright 2012 Gary Burd
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License"): you may
  4. // not use this file except in compliance with the License. You may obtain
  5. // a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  11. // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  12. // License for the specific language governing permissions and limitations
  13. // under the License.
  14. package scan
  15. import (
  16. "errors"
  17. "reflect"
  18. "strconv"
  19. )
  20. // ScanStruct scans a reply containing alternating names and values to a
  21. // struct. The HGETALL and CONFIG GET commands return replies in this format.
  22. //
  23. // ScanStruct uses the struct field name to match values in the response. Use
  24. // 'redis' field tag to override the name:
  25. //
  26. // Field int `redis:"myName"`
  27. //
  28. // Fields with the tag redis:"-" are ignored.
  29. func ScanStruct(reply interface{}, dst interface{}) error {
  30. v := reflect.ValueOf(dst)
  31. if v.Kind() != reflect.Ptr || v.IsNil() {
  32. return errors.New("redigo: ScanStruct value must be non-nil pointer")
  33. }
  34. v = v.Elem()
  35. ss := structSpecForType(v.Type())
  36. p, ok := reply.([]interface{})
  37. if !ok {
  38. return errors.New("redigo: ScanStruct expectes multibulk reply")
  39. }
  40. if len(p)%2 != 0 {
  41. return errors.New("redigo: ScanStruct expects even number of values in reply")
  42. }
  43. for i := 0; i < len(p); i += 2 {
  44. name, ok := p[i].([]byte)
  45. if !ok {
  46. return errors.New("redigo: ScanStruct key not a bulk value")
  47. }
  48. value, ok := p[i+1].([]byte)
  49. if !ok {
  50. return errors.New("redigo: ScanStruct value not a bulk value")
  51. }
  52. fs := ss.fieldSpec(name)
  53. if fs == nil {
  54. continue
  55. }
  56. fv := v.FieldByIndex(fs.index)
  57. switch fv.Type().Kind() {
  58. case reflect.String:
  59. fv.SetString(string(value))
  60. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  61. x, err := strconv.ParseInt(string(value), 10, fv.Type().Bits())
  62. if err != nil {
  63. return err
  64. }
  65. fv.SetInt(x)
  66. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  67. x, err := strconv.ParseUint(string(value), 10, fv.Type().Bits())
  68. if err != nil {
  69. return err
  70. }
  71. fv.SetUint(x)
  72. case reflect.Float32, reflect.Float64:
  73. x, err := strconv.ParseFloat(string(value), fv.Type().Bits())
  74. if err != nil {
  75. return err
  76. }
  77. fv.SetFloat(x)
  78. case reflect.Bool:
  79. x := len(value) != 0 && (len(value) != 1 || value[0] != '0')
  80. fv.SetBool(x)
  81. case reflect.Slice:
  82. if fv.Type().Elem().Kind() != reflect.Uint8 {
  83. // TODO: check field types in structSpec
  84. panic("redigo: unsuported type for field " + string(name))
  85. }
  86. fv.SetBytes(value)
  87. default:
  88. // TODO: check field types in structSpec
  89. panic("redigo: unsuported type for field " + string(name))
  90. }
  91. }
  92. return nil
  93. }
  94. func FormatStruct(src interface{}) []interface{} {
  95. v := reflect.ValueOf(src)
  96. if v.Kind() == reflect.Ptr {
  97. if v.IsNil() {
  98. panic("redigo: FormatStruct argument must not be nil")
  99. }
  100. v = v.Elem()
  101. }
  102. if v.Kind() != reflect.Struct {
  103. panic("redigo: FormatStruct argument must be a struct or pointer to a struct")
  104. }
  105. ss := structSpecForType(v.Type())
  106. result := make([]interface{}, 0, 2*len(ss.l))
  107. for _, fs := range ss.l {
  108. fv := v.FieldByIndex(fs.index)
  109. result = append(result, fs.name, fv.Interface())
  110. }
  111. return result
  112. }