// Package reply implements the Redis Serialization Protocol (RESP) reply handling functionality. // // This package contains implementations for building various response formats that comply with the Redis RESP protocol, supporting the following types: // - Simple Strings: StatusReply // - Errors: StandardErrReply and its subtypes // - Integers: IntReply // - Bulk Strings: BulkReply // - Arrays: MultiBulkReply // // 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. // // Example usage: // // Create an integer reply // // reply := NewIntReply(42) // output := reply.ToBytes() // Output ":42\r\n" // // Create a bulk string reply // // bulk := NewBulkReply([]byte("hello")) // output := bulk.ToBytes() // Output "$5\r\nhello\r\n" package reply import ( "bytes" "strconv" "github.com/runningwater/go-redis/interface/resp" ) var ( nullBulkReplyBytes = []byte("$-1") CRLF = "\r\n" ) // BulkReply represents a bulk string reply structure. type BulkReply struct { Arg []byte // "test" => "$4\r\ntest\r\n" } func (b *BulkReply) String() string { return string(b.ToBytes()) } func (b *BulkReply) ToBytes() []byte { if len(b.Arg) == 0 { return nullBulkReplyBytes } return []byte("$" + strconv.Itoa(len(b.Arg)) + CRLF + string(b.Arg) + CRLF) } // NewBulkReply creates a new bulk reply func NewBulkReply(arg []byte) *BulkReply { return &BulkReply{Arg: arg} } // MultiBulkReply represents a multi-bulk string reply structure. type MultiBulkReply struct { Args [][]byte } func (m *MultiBulkReply) String() string { return string(m.ToBytes()) } func (m *MultiBulkReply) ToBytes() []byte { argLen := len(m.Args) var buf bytes.Buffer buf.WriteString("*" + strconv.Itoa(argLen) + CRLF) for _, arg := range m.Args { if arg == nil { buf.WriteString(string(nullBulkReplyBytes) + CRLF) } else { buf.WriteString("$" + strconv.Itoa(len(arg)) + CRLF + string(arg) + CRLF) } } return buf.Bytes() } // NewMultiBulkReply creates a new multi-bulk reply func NewMultiBulkReply(args [][]byte) *MultiBulkReply { return &MultiBulkReply{Args: args} } // StatusReply represents a simple string reply structure. // For example: OK, PING, SET, GET command replies. // Corresponds to Redis replies starting with "+". // For example: "+OK\r\n" type StatusReply struct { Status string } func (s *StatusReply) String() string { return string(s.ToBytes()) } func (s *StatusReply) ToBytes() []byte { return []byte("+" + s.Status + CRLF) } // NewStatusReply creates a new status reply func NewStatusReply(status string) *StatusReply { return &StatusReply{Status: status} } // IntReply represents an integer reply structure. // Corresponds to Redis replies starting with ":". // For example: ":1\r\n" type IntReply struct { Code int64 } func (i *IntReply) String() string { return string(i.ToBytes()) } func (i *IntReply) ToBytes() []byte { return []byte(":" + strconv.FormatInt(i.Code, 10) + CRLF) } // NewIntReply creates a new integer reply func NewIntReply(code int64) *IntReply { return &IntReply{Code: code} } // ErrorReply represents an error reply interface. type ErrorReply interface { Error() string ToBytes() []byte } // StandardErrReply represents a standard error reply structure. // Corresponds to Redis replies starting with "-". // For example: "-ERR unknown command 'foobar'\r\n" type StandardErrReply struct { Status string } func (s *StandardErrReply) String() string { return string(s.ToBytes()) } func (s *StandardErrReply) Error() string { return s.Status } func (s *StandardErrReply) ToBytes() []byte { return []byte("-" + s.Status + CRLF) } // NewErrReply creates a new error reply func NewErrReply(status string) *StandardErrReply { return &StandardErrReply{Status: status} } // IsErrReply checks if the reply is an error reply. // Error replies start with "-". // For example: "-ERR unknown command 'foobar'\r\n" func IsErrReply(reply resp.Reply) bool { toBytes := reply.ToBytes() return len(toBytes) > 0 && toBytes[0] == '-' }