reply.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. // Package reply implements the Redis Serialization Protocol (RESP) reply handling functionality.
  2. //
  3. // This package contains implementations for building various response formats that comply with the Redis RESP protocol, supporting the following types:
  4. // - Simple Strings: StatusReply
  5. // - Errors: StandardErrReply and its subtypes
  6. // - Integers: IntReply
  7. // - Bulk Strings: BulkReply
  8. // - Arrays: MultiBulkReply
  9. //
  10. // All reply types implement the [resp.Reply](file:///Users/lixiaoming/go/src/github.com/runningwater/go-redis/interface/resp/reply.go#L7-L12) interface, generating byte data in the RESP protocol format through the [ToBytes()](file:///Users/lixiaoming/go/src/github.com/runningwater/go-redis/interface/resp/reply.go#L9-L9) method.
  11. //
  12. // Example usage:
  13. //
  14. // Create an integer reply
  15. //
  16. // reply := NewIntReply(42)
  17. // output := reply.ToBytes() // Output ":42\r\n"
  18. //
  19. // Create a bulk string reply
  20. //
  21. // bulk := NewBulkReply([]byte("hello"))
  22. // output := bulk.ToBytes() // Output "$5\r\nhello\r\n"
  23. package reply
  24. import (
  25. "bytes"
  26. "strconv"
  27. "github.com/runningwater/go-redis/interface/resp"
  28. )
  29. var (
  30. nullBulkReplyBytes = []byte("$-1")
  31. CRLF = "\r\n"
  32. )
  33. // BulkReply represents a bulk string reply structure.
  34. type BulkReply struct {
  35. Arg []byte // "test" => "$4\r\ntest\r\n"
  36. }
  37. func (b *BulkReply) Error() string {
  38. return ""
  39. }
  40. func (b *BulkReply) String() string {
  41. return string(b.ToBytes())
  42. }
  43. func (b *BulkReply) ToBytes() []byte {
  44. if len(b.Arg) == 0 {
  45. return nullBulkReplyBytes
  46. }
  47. return []byte("$" + strconv.Itoa(len(b.Arg)) + CRLF + string(b.Arg) + CRLF)
  48. }
  49. // NewBulkReply creates a new bulk reply
  50. func NewBulkReply(arg []byte) *BulkReply {
  51. return &BulkReply{Arg: arg}
  52. }
  53. // MultiBulkReply represents a multi-bulk string reply structure.
  54. type MultiBulkReply struct {
  55. Args [][]byte
  56. }
  57. func (m *MultiBulkReply) Error() string {
  58. return ""
  59. }
  60. func (m *MultiBulkReply) String() string {
  61. return string(m.ToBytes())
  62. }
  63. func (m *MultiBulkReply) ToBytes() []byte {
  64. argLen := len(m.Args)
  65. var buf bytes.Buffer
  66. buf.WriteString("*" + strconv.Itoa(argLen) + CRLF)
  67. for _, arg := range m.Args {
  68. if arg == nil {
  69. buf.WriteString(string(nullBulkReplyBytes) + CRLF)
  70. } else {
  71. buf.WriteString("$" + strconv.Itoa(len(arg)) + CRLF + string(arg) + CRLF)
  72. }
  73. }
  74. return buf.Bytes()
  75. }
  76. // NewMultiBulkReply creates a new multi-bulk reply
  77. func NewMultiBulkReply(args [][]byte) *MultiBulkReply {
  78. return &MultiBulkReply{Args: args}
  79. }
  80. // StatusReply represents a simple string reply structure.
  81. // For example: OK, PING, SET, GET command replies.
  82. // Corresponds to Redis replies starting with "+".
  83. // For example: "+OK\r\n"
  84. type StatusReply struct {
  85. Status string
  86. }
  87. func (s *StatusReply) Error() string {
  88. return ""
  89. }
  90. func (s *StatusReply) String() string {
  91. return string(s.ToBytes())
  92. }
  93. func (s *StatusReply) ToBytes() []byte {
  94. return []byte("+" + s.Status + CRLF)
  95. }
  96. // NewStatusReply creates a new status reply
  97. func NewStatusReply(status string) *StatusReply {
  98. return &StatusReply{Status: status}
  99. }
  100. // IntReply represents an integer reply structure.
  101. // Corresponds to Redis replies starting with ":".
  102. // For example: ":1\r\n"
  103. type IntReply struct {
  104. Code int64
  105. }
  106. func (i *IntReply) Error() string {
  107. return ""
  108. }
  109. func (i *IntReply) String() string {
  110. return string(i.ToBytes())
  111. }
  112. func (i *IntReply) ToBytes() []byte {
  113. return []byte(":" + strconv.FormatInt(i.Code, 10) + CRLF)
  114. }
  115. // NewIntReply creates a new integer reply
  116. func NewIntReply(code int64) *IntReply {
  117. return &IntReply{Code: code}
  118. }
  119. // StandardErrReply represents a standard error reply structure.
  120. // Corresponds to Redis replies starting with "-".
  121. // For example: "-ERR unknown command 'foobar'\r\n"
  122. type StandardErrReply struct {
  123. Status string
  124. }
  125. func (s *StandardErrReply) String() string {
  126. return string(s.ToBytes())
  127. }
  128. func (s *StandardErrReply) Error() string {
  129. return s.Status
  130. }
  131. func (s *StandardErrReply) ToBytes() []byte {
  132. return []byte("-" + s.Status + CRLF)
  133. }
  134. // NewErrReply creates a new error reply
  135. func NewErrReply(status string) *StandardErrReply {
  136. return &StandardErrReply{Status: status}
  137. }
  138. // IsErrReply checks if the reply is an error reply.
  139. // Error replies start with "-".
  140. // For example: "-ERR unknown command 'foobar'\r\n"
  141. func IsErrReply(reply resp.Reply) bool {
  142. toBytes := reply.ToBytes()
  143. return len(toBytes) > 0 && toBytes[0] == '-'
  144. }