github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/database/redis/reply.go (about) 1 // Copyright 2014 <chaishushan{AT}gmail.com>. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package redis 6 7 import ( 8 "errors" 9 "strconv" 10 ) 11 12 //* Reply 13 14 /* 15 ReplyType describes type of a reply. 16 17 Possible values are: 18 19 StatusReply -- status reply 20 ErrorReply -- error reply 21 IntegerReply -- integer reply 22 NilReply -- nil reply 23 BulkReply -- bulk reply 24 MultiReply -- multi bulk reply 25 */ 26 type ReplyType uint8 27 28 const ( 29 StatusReply ReplyType = iota 30 ErrorReply 31 IntegerReply 32 NilReply 33 BulkReply 34 MultiReply 35 ) 36 37 // Reply holds a Redis reply. 38 type Reply struct { 39 Type ReplyType // Reply type 40 Elems []*Reply // Sub-replies 41 Err error // Reply error 42 buf []byte 43 int int64 44 } 45 46 // Bytes returns the reply value as a byte string or 47 // an error, if the reply type is not StatusReply or BulkReply. 48 func (r *Reply) Bytes() ([]byte, error) { 49 if r.Type == ErrorReply { 50 return nil, r.Err 51 } 52 if !(r.Type == StatusReply || r.Type == BulkReply) { 53 return nil, errors.New("string value is not available for this reply type") 54 } 55 56 return r.buf, nil 57 } 58 59 // Str is a convenience method for calling Reply.Bytes() and converting it to string 60 func (r *Reply) Str() (string, error) { 61 b, err := r.Bytes() 62 if err != nil { 63 return "", err 64 } 65 66 return string(b), nil 67 } 68 69 // Int64 returns the reply value as a int64 or an error, 70 // if the reply type is not IntegerReply or the reply type 71 // BulkReply could not be parsed to an int64. 72 func (r *Reply) Int64() (int64, error) { 73 if r.Type == ErrorReply { 74 return 0, r.Err 75 } 76 if r.Type != IntegerReply { 77 s, err := r.Str() 78 if err == nil { 79 i64, err := strconv.ParseInt(s, 10, 64) 80 if err != nil { 81 return 0, errors.New("failed to parse integer value from string value") 82 } else { 83 return i64, nil 84 } 85 } 86 87 return 0, errors.New("integer value is not available for this reply type") 88 } 89 90 return r.int, nil 91 } 92 93 // Int is a convenience method for calling Reply.Int64() and converting it to int. 94 func (r *Reply) Int() (int, error) { 95 i64, err := r.Int64() 96 if err != nil { 97 return 0, err 98 } 99 100 return int(i64), nil 101 } 102 103 // Bool returns false, if the reply value equals to 0 or "0", otherwise true; or 104 // an error, if the reply type is not IntegerReply or BulkReply. 105 func (r *Reply) Bool() (bool, error) { 106 if r.Type == ErrorReply { 107 return false, r.Err 108 } 109 i, err := r.Int() 110 if err == nil { 111 if i == 0 { 112 return false, nil 113 } 114 115 return true, nil 116 } 117 118 s, err := r.Str() 119 if err == nil { 120 if s == "0" { 121 return false, nil 122 } 123 124 return true, nil 125 } 126 127 return false, errors.New("boolean value is not available for this reply type") 128 } 129 130 // List returns a multi bulk reply as a slice of strings or an error. 131 // The reply type must be MultiReply and its elements' types must all be either BulkReply or NilReply. 132 // Nil elements are returned as empty strings. 133 // Useful for list commands. 134 func (r *Reply) List() ([]string, error) { 135 // Doing all this in two places instead of just calling ListBytes() so we don't have 136 // to iterate twice 137 if r.Type == ErrorReply { 138 return nil, r.Err 139 } 140 if r.Type != MultiReply { 141 return nil, errors.New("reply type is not MultiReply") 142 } 143 144 strings := make([]string, len(r.Elems)) 145 for i, v := range r.Elems { 146 if v.Type == BulkReply { 147 strings[i] = string(v.buf) 148 } else if v.Type == NilReply { 149 strings[i] = "" 150 } else { 151 return nil, errors.New("element type is not BulkReply or NilReply") 152 } 153 } 154 155 return strings, nil 156 } 157 158 // ListBytes returns a multi bulk reply as a slice of bytes slices or an error. 159 // The reply type must be MultiReply and its elements' types must all be either BulkReply or NilReply. 160 // Nil elements are returned as nil. 161 // Useful for list commands. 162 func (r *Reply) ListBytes() ([][]byte, error) { 163 if r.Type == ErrorReply { 164 return nil, r.Err 165 } 166 if r.Type != MultiReply { 167 return nil, errors.New("reply type is not MultiReply") 168 } 169 170 bufs := make([][]byte, len(r.Elems)) 171 for i, v := range r.Elems { 172 if v.Type == BulkReply { 173 bufs[i] = v.buf 174 } else if v.Type == NilReply { 175 bufs[i] = nil 176 } else { 177 return nil, errors.New("element type is not BulkReply or NilReply") 178 } 179 } 180 181 return bufs, nil 182 } 183 184 // Hash returns a multi bulk reply as a map[string]string or an error. 185 // The reply type must be MultiReply, 186 // it must have an even number of elements, 187 // they must be in a "key value key value..." order and 188 // values must all be either BulkReply or NilReply. 189 // Nil values are returned as empty strings. 190 // Useful for hash commands. 191 func (r *Reply) Hash() (map[string]string, error) { 192 if r.Type == ErrorReply { 193 return nil, r.Err 194 } 195 rmap := map[string]string{} 196 197 if r.Type != MultiReply { 198 return nil, errors.New("reply type is not MultiReply") 199 } 200 201 if len(r.Elems)%2 != 0 { 202 return nil, errors.New("reply has odd number of elements") 203 } 204 205 for i := 0; i < len(r.Elems)/2; i++ { 206 var val string 207 208 key, err := r.Elems[i*2].Str() 209 if err != nil { 210 return nil, errors.New("key element has no string reply") 211 } 212 213 v := r.Elems[i*2+1] 214 if v.Type == BulkReply { 215 val = string(v.buf) 216 rmap[key] = val 217 } else if v.Type == NilReply { 218 } else { 219 return nil, errors.New("value element type is not BulkReply or NilReply") 220 } 221 } 222 223 return rmap, nil 224 } 225 226 // String returns a string representation of the reply and its sub-replies. 227 // This method is for debugging. 228 // Use method Reply.Str() for reading string reply. 229 func (r *Reply) String() string { 230 switch r.Type { 231 case ErrorReply: 232 return r.Err.Error() 233 case StatusReply: 234 fallthrough 235 case BulkReply: 236 return string(r.buf) 237 case IntegerReply: 238 return strconv.FormatInt(r.int, 10) 239 case NilReply: 240 return "<nil>" 241 case MultiReply: 242 s := "[ " 243 for _, e := range r.Elems { 244 s = s + e.String() + " " 245 } 246 return s + "]" 247 } 248 249 // This should never execute 250 return "" 251 }