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 15package redis 16 17import ( 18 "errors" 19 "fmt" 20 "strconv" 21) 22 23// ErrNil indicates that a reply value is nil. 24var ErrNil = errors.New("redigo: nil returned") 25 26// Int is a helper that converts a command reply to an integer. If err is not 27// equal to nil, then Int returns 0, err. Otherwise, Int converts the 28// reply to an int as follows: 29// 30// Reply type Result 31// integer int(reply), nil 32// bulk string parsed reply, nil 33// nil 0, ErrNil 34// other 0, error 35func Int(reply interface{}, err error) (int, error) { 36 if err != nil { 37 return 0, err 38 } 39 switch reply := reply.(type) { 40 case int64: 41 x := int(reply) 42 if int64(x) != reply { 43 return 0, strconv.ErrRange 44 } 45 return x, nil 46 case []byte: 47 n, err := strconv.ParseInt(string(reply), 10, 0) 48 return int(n), err 49 case nil: 50 return 0, ErrNil 51 case Error: 52 return 0, reply 53 } 54 return 0, fmt.Errorf("redigo: unexpected type for Int, got type %T", reply) 55} 56 57// Int64 is a helper that converts a command reply to 64 bit integer. If err is 58// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the 59// reply to an int64 as follows: 60// 61// Reply type Result 62// integer reply, nil 63// bulk string parsed reply, nil 64// nil 0, ErrNil 65// other 0, error 66func Int64(reply interface{}, err error) (int64, error) { 67 if err != nil { 68 return 0, err 69 } 70 switch reply := reply.(type) { 71 case int64: 72 return reply, nil 73 case []byte: 74 n, err := strconv.ParseInt(string(reply), 10, 64) 75 return n, err 76 case nil: 77 return 0, ErrNil 78 case Error: 79 return 0, reply 80 } 81 return 0, fmt.Errorf("redigo: unexpected type for Int64, got type %T", reply) 82} 83 84var errNegativeInt = errors.New("redigo: unexpected value for Uint64") 85 86// Uint64 is a helper that converts a command reply to 64 bit integer. If err is 87// not equal to nil, then Int returns 0, err. Otherwise, Int64 converts the 88// reply to an int64 as follows: 89// 90// Reply type Result 91// integer reply, nil 92// bulk string parsed reply, nil 93// nil 0, ErrNil 94// other 0, error 95func Uint64(reply interface{}, err error) (uint64, error) { 96 if err != nil { 97 return 0, err 98 } 99 switch reply := reply.(type) { 100 case int64: 101 if reply < 0 { 102 return 0, errNegativeInt 103 } 104 return uint64(reply), nil 105 case []byte: 106 n, err := strconv.ParseUint(string(reply), 10, 64) 107 return n, err 108 case nil: 109 return 0, ErrNil 110 case Error: 111 return 0, reply 112 } 113 return 0, fmt.Errorf("redigo: unexpected type for Uint64, got type %T", reply) 114} 115 116// Float64 is a helper that converts a command reply to 64 bit float. If err is 117// not equal to nil, then Float64 returns 0, err. Otherwise, Float64 converts 118// the reply to an int as follows: 119// 120// Reply type Result 121// bulk string parsed reply, nil 122// nil 0, ErrNil 123// other 0, error 124func Float64(reply interface{}, err error) (float64, error) { 125 if err != nil { 126 return 0, err 127 } 128 switch reply := reply.(type) { 129 case []byte: 130 n, err := strconv.ParseFloat(string(reply), 64) 131 return n, err 132 case nil: 133 return 0, ErrNil 134 case Error: 135 return 0, reply 136 } 137 return 0, fmt.Errorf("redigo: unexpected type for Float64, got type %T", reply) 138} 139 140// String is a helper that converts a command reply to a string. If err is not 141// equal to nil, then String returns "", err. Otherwise String converts the 142// reply to a string as follows: 143// 144// Reply type Result 145// bulk string string(reply), nil 146// simple string reply, nil 147// nil "", ErrNil 148// other "", error 149func String(reply interface{}, err error) (string, error) { 150 if err != nil { 151 return "", err 152 } 153 switch reply := reply.(type) { 154 case []byte: 155 return string(reply), nil 156 case string: 157 return reply, nil 158 case nil: 159 return "", ErrNil 160 case Error: 161 return "", reply 162 } 163 return "", fmt.Errorf("redigo: unexpected type for String, got type %T", reply) 164} 165 166// Bytes is a helper that converts a command reply to a slice of bytes. If err 167// is not equal to nil, then Bytes returns nil, err. Otherwise Bytes converts 168// the reply to a slice of bytes as follows: 169// 170// Reply type Result 171// bulk string reply, nil 172// simple string []byte(reply), nil 173// nil nil, ErrNil 174// other nil, error 175func Bytes(reply interface{}, err error) ([]byte, error) { 176 if err != nil { 177 return nil, err 178 } 179 switch reply := reply.(type) { 180 case []byte: 181 return reply, nil 182 case string: 183 return []byte(reply), nil 184 case nil: 185 return nil, ErrNil 186 case Error: 187 return nil, reply 188 } 189 return nil, fmt.Errorf("redigo: unexpected type for Bytes, got type %T", reply) 190} 191 192// Bool is a helper that converts a command reply to a boolean. If err is not 193// equal to nil, then Bool returns false, err. Otherwise Bool converts the 194// reply to boolean as follows: 195// 196// Reply type Result 197// integer value != 0, nil 198// bulk string strconv.ParseBool(reply) 199// nil false, ErrNil 200// other false, error 201func Bool(reply interface{}, err error) (bool, error) { 202 if err != nil { 203 return false, err 204 } 205 switch reply := reply.(type) { 206 case int64: 207 return reply != 0, nil 208 case []byte: 209 return strconv.ParseBool(string(reply)) 210 case nil: 211 return false, ErrNil 212 case Error: 213 return false, reply 214 } 215 return false, fmt.Errorf("redigo: unexpected type for Bool, got type %T", reply) 216} 217 218// MultiBulk is deprecated. Use Values. 219func MultiBulk(reply interface{}, err error) ([]interface{}, error) { return Values(reply, err) } 220 221// Values is a helper that converts an array command reply to a []interface{}. 222// If err is not equal to nil, then Values returns nil, err. Otherwise, Values 223// converts the reply as follows: 224// 225// Reply type Result 226// array reply, nil 227// nil nil, ErrNil 228// other nil, error 229func Values(reply interface{}, err error) ([]interface{}, error) { 230 if err != nil { 231 return nil, err 232 } 233 switch reply := reply.(type) { 234 case []interface{}: 235 return reply, nil 236 case nil: 237 return nil, ErrNil 238 case Error: 239 return nil, reply 240 } 241 return nil, fmt.Errorf("redigo: unexpected type for Values, got type %T", reply) 242} 243 244// Strings is a helper that converts an array command reply to a []string. If 245// err is not equal to nil, then Strings returns nil, err. Nil array items are 246// converted to "" in the output slice. Strings returns an error if an array 247// item is not a bulk string or nil. 248func Strings(reply interface{}, err error) ([]string, error) { 249 if err != nil { 250 return nil, err 251 } 252 switch reply := reply.(type) { 253 case []interface{}: 254 result := make([]string, len(reply)) 255 for i := range reply { 256 if reply[i] == nil { 257 continue 258 } 259 p, ok := reply[i].([]byte) 260 if !ok { 261 return nil, fmt.Errorf("redigo: unexpected element type for Strings, got type %T", reply[i]) 262 } 263 result[i] = string(p) 264 } 265 return result, nil 266 case nil: 267 return nil, ErrNil 268 case Error: 269 return nil, reply 270 } 271 return nil, fmt.Errorf("redigo: unexpected type for Strings, got type %T", reply) 272} 273 274// Ints is a helper that converts an array command reply to a []int. If 275// err is not equal to nil, then Ints returns nil, err. 276func Ints(reply interface{}, err error) ([]int, error) { 277 var ints []int 278 if reply == nil { 279 return ints, ErrNil 280 } 281 values, err := Values(reply, err) 282 if err != nil { 283 return ints, err 284 } 285 if err := ScanSlice(values, &ints); err != nil { 286 return ints, err 287 } 288 return ints, nil 289} 290 291// StringMap is a helper that converts an array of strings (alternating key, value) 292// into a map[string]string. The HGETALL and CONFIG GET commands return replies in this format. 293// Requires an even number of values in result. 294func StringMap(result interface{}, err error) (map[string]string, error) { 295 values, err := Values(result, err) 296 if err != nil { 297 return nil, err 298 } 299 if len(values)%2 != 0 { 300 return nil, errors.New("redigo: StringMap expects even number of values result") 301 } 302 m := make(map[string]string, len(values)/2) 303 for i := 0; i < len(values); i += 2 { 304 key, okKey := values[i].([]byte) 305 value, okValue := values[i+1].([]byte) 306 if !okKey || !okValue { 307 return nil, errors.New("redigo: ScanMap key not a bulk string value") 308 } 309 m[string(key)] = string(value) 310 } 311 return m, nil 312} 313