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  }