scan.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  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 redis
  15. import (
  16. "errors"
  17. "fmt"
  18. "reflect"
  19. "strconv"
  20. )
  21. func cannotConvert(d reflect.Value, s interface{}) error {
  22. return fmt.Errorf("redigo: Scan cannot convert from %s to %s",
  23. reflect.TypeOf(s), d.Type())
  24. }
  25. func convertAssignBulk(d reflect.Value, s []byte) (err error) {
  26. switch d.Type().Kind() {
  27. case reflect.Float32, reflect.Float64:
  28. var x float64
  29. x, err = strconv.ParseFloat(string(s), d.Type().Bits())
  30. d.SetFloat(x)
  31. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  32. var x int64
  33. x, err = strconv.ParseInt(string(s), 10, d.Type().Bits())
  34. d.SetInt(x)
  35. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  36. var x uint64
  37. x, err = strconv.ParseUint(string(s), 10, d.Type().Bits())
  38. d.SetUint(x)
  39. case reflect.Bool:
  40. var x bool
  41. x, err = strconv.ParseBool(string(s))
  42. d.SetBool(x)
  43. case reflect.String:
  44. d.SetString(string(s))
  45. case reflect.Slice:
  46. if d.Type().Elem().Kind() != reflect.Uint8 {
  47. err = cannotConvert(d, s)
  48. } else {
  49. d.SetBytes(s)
  50. }
  51. default:
  52. err = cannotConvert(d, s)
  53. }
  54. return
  55. }
  56. func convertAssignInt(d reflect.Value, s int64) (err error) {
  57. switch d.Type().Kind() {
  58. case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
  59. d.SetInt(s)
  60. if d.Int() != s {
  61. err = strconv.ErrRange
  62. d.SetInt(0)
  63. }
  64. case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
  65. if s < 0 {
  66. err = strconv.ErrRange
  67. } else {
  68. x := uint64(s)
  69. d.SetUint(x)
  70. if d.Uint() != x {
  71. err = strconv.ErrRange
  72. d.SetUint(0)
  73. }
  74. }
  75. case reflect.Bool:
  76. d.SetBool(s != 0)
  77. default:
  78. err = cannotConvert(d, s)
  79. }
  80. return
  81. }
  82. // Scan copies from a multi-bulk command reply to the values pointed at by
  83. // dest.
  84. //
  85. // The values pointed at by test must be a numeric type, boolean, string or a
  86. // byte slice. Scan uses the standard strconv package to convert bulk values to
  87. // numeric and boolean types.
  88. //
  89. // If the multi-bulk value is nil, then the corresponding dest value is not
  90. // modified.
  91. //
  92. // To enable easy use of Scan in a loop, Scan returns the slice following the
  93. // copied values.
  94. func Scan(multiBulk []interface{}, dest ...interface{}) ([]interface{}, error) {
  95. // We handle the most common dest types using type switches and fallback to
  96. // reflection for all other types.
  97. if len(multiBulk) < len(dest) {
  98. return nil, errors.New("redigo: Scan multibulk short")
  99. }
  100. var err error
  101. for i, d := range dest {
  102. switch s := multiBulk[i].(type) {
  103. case nil:
  104. // ingore
  105. case []byte:
  106. switch d := d.(type) {
  107. case *string:
  108. *d = string(s)
  109. case *int:
  110. *d, err = strconv.Atoi(string(s))
  111. case *bool:
  112. *d, err = strconv.ParseBool(string(s))
  113. case *[]byte:
  114. *d = s
  115. default:
  116. if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
  117. err = cannotConvert(d, s)
  118. } else {
  119. err = convertAssignBulk(d.Elem(), s)
  120. }
  121. }
  122. case int64:
  123. switch d := d.(type) {
  124. case *int:
  125. x := int(s)
  126. if int64(x) != s {
  127. err = strconv.ErrRange
  128. x = 0
  129. }
  130. *d = x
  131. case *bool:
  132. *d = s != 0
  133. default:
  134. if d := reflect.ValueOf(d); d.Type().Kind() != reflect.Ptr {
  135. err = cannotConvert(d, s)
  136. } else {
  137. err = convertAssignInt(d.Elem(), s)
  138. }
  139. }
  140. default:
  141. err = cannotConvert(reflect.ValueOf(d), s)
  142. }
  143. if err != nil {
  144. break
  145. }
  146. }
  147. return multiBulk[len(dest):], err
  148. }