reply.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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) String() string {
  38. return string(b.ToBytes())
  39. }
  40. func (b *BulkReply) ToBytes() []byte {
  41. if len(b.Arg) == 0 {
  42. return nullBulkReplyBytes
  43. }
  44. return []byte("$" + strconv.Itoa(len(b.Arg)) + CRLF + string(b.Arg) + CRLF)
  45. }
  46. // NewBulkReply creates a new bulk reply
  47. func NewBulkReply(arg []byte) *BulkReply {
  48. return &BulkReply{Arg: arg}
  49. }
  50. // MultiBulkReply represents a multi-bulk string reply structure.
  51. type MultiBulkReply struct {
  52. Args [][]byte
  53. }
  54. func (m *MultiBulkReply) String() string {
  55. return string(m.ToBytes())
  56. }
  57. func (m *MultiBulkReply) ToBytes() []byte {
  58. argLen := len(m.Args)
  59. var buf bytes.Buffer
  60. buf.WriteString("*" + strconv.Itoa(argLen) + CRLF)
  61. for _, arg := range m.Args {
  62. if arg == nil {
  63. buf.WriteString(string(nullBulkReplyBytes) + CRLF)
  64. } else {
  65. buf.WriteString("$" + strconv.Itoa(len(arg)) + CRLF + string(arg) + CRLF)
  66. }
  67. }
  68. return buf.Bytes()
  69. }
  70. // NewMultiBulkReply creates a new multi-bulk reply
  71. func NewMultiBulkReply(args [][]byte) *MultiBulkReply {
  72. return &MultiBulkReply{Args: args}
  73. }
  74. // StatusReply represents a simple string reply structure.
  75. // For example: OK, PING, SET, GET command replies.
  76. // Corresponds to Redis replies starting with "+".
  77. // For example: "+OK\r\n"
  78. type StatusReply struct {
  79. Status string
  80. }
  81. func (s *StatusReply) String() string {
  82. return string(s.ToBytes())
  83. }
  84. func (s *StatusReply) ToBytes() []byte {
  85. return []byte("+" + s.Status + CRLF)
  86. }
  87. // NewStatusReply creates a new status reply
  88. func NewStatusReply(status string) *StatusReply {
  89. return &StatusReply{Status: status}
  90. }
  91. // IntReply represents an integer reply structure.
  92. // Corresponds to Redis replies starting with ":".
  93. // For example: ":1\r\n"
  94. type IntReply struct {
  95. Code int64
  96. }
  97. func (i *IntReply) String() string {
  98. return string(i.ToBytes())
  99. }
  100. func (i *IntReply) ToBytes() []byte {
  101. return []byte(":" + strconv.FormatInt(i.Code, 10) + CRLF)
  102. }
  103. // NewIntReply creates a new integer reply
  104. func NewIntReply(code int64) *IntReply {
  105. return &IntReply{Code: code}
  106. }
  107. // ErrorReply represents an error reply interface.
  108. type ErrorReply interface {
  109. Error() string
  110. ToBytes() []byte
  111. }
  112. // StandardErrReply represents a standard error reply structure.
  113. // Corresponds to Redis replies starting with "-".
  114. // For example: "-ERR unknown command 'foobar'\r\n"
  115. type StandardErrReply struct {
  116. Status string
  117. }
  118. func (s *StandardErrReply) String() string {
  119. return string(s.ToBytes())
  120. }
  121. func (s *StandardErrReply) Error() string {
  122. return s.Status
  123. }
  124. func (s *StandardErrReply) ToBytes() []byte {
  125. return []byte("-" + s.Status + CRLF)
  126. }
  127. // NewErrReply creates a new error reply
  128. func NewErrReply(status string) *StandardErrReply {
  129. return &StandardErrReply{Status: status}
  130. }
  131. // IsErrReply checks if the reply is an error reply.
  132. // Error replies start with "-".
  133. // For example: "-ERR unknown command 'foobar'\r\n"
  134. func IsErrReply(reply resp.Reply) bool {
  135. toBytes := reply.ToBytes()
  136. return len(toBytes) > 0 && toBytes[0] == '-'
  137. }