/
main.go
135 lines (115 loc) · 2.29 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
package main
import (
"bufio"
"bytes"
"fmt"
"net"
"strconv"
)
const (
PING = "PING"
SET = "SET"
GET = "GET"
)
// can optimize to
const (
SIMPLE_STRING byte = '+'
ERRORS byte = '-'
INTEGER byte = ':'
BULK_STRING byte = '$'
ARRAY byte = '*'
)
type RClient struct {
net.Conn
rx chan string
}
func NewClient(serverAddr string) *RClient {
conn, err := net.Dial("tcp", serverAddr)
if err != nil {
return nil
}
rClient := &RClient{conn, make(chan string)}
go func() {
rClient.readTx()
}()
return rClient
}
func (c RClient) Ping() (string, error) {
c.sendCmd(PING)
res, err := c.readResponse()
return res.(string), err
}
func (c RClient) Set(key, val string) bool {
cmd := fmt.Sprintf("%s %s %s", SET, key, val)
c.sendCmd(cmd)
res, _ := c.readResponse()
if res.(string) == "OK" {
return true
}
return false
}
func (c RClient) Get(key string) string {
cmd := fmt.Sprintf("%s %s", GET, key)
c.sendCmd(cmd)
res, _ := c.readResponse()
return res.(string)
}
const CRLF = "\r\n"
func (c RClient) sendCmd(cmd string) {
c.Write([]byte(cmd + CRLF))
return
}
func (c RClient) readTx() {
sc := bufio.NewScanner(c)
sc.Split(ScanCRLF)
for sc.Scan() {
c.rx <- sc.Text()
}
}
func (c RClient) readResponse() (interface{}, error) {
token := <-c.rx
//type
switch token[0] {
case SIMPLE_STRING, ERRORS:
return token[1:], nil
case INTEGER:
val, err := strconv.Atoi(token[1:])
if err != nil {
return 0, err
}
return val, nil
case ARRAY:
// TODO
case BULK_STRING:
str := <-c.rx
return str, nil
}
return nil, nil
}
func main() {
rc := NewClient("127.0.0.1:6379")
fmt.Println(rc.Set("hi", "world"))
fmt.Println(rc.Get("hi"))
}
// dropCR drops a terminal \r from the data.
func dropCR(data []byte) []byte {
if len(data) > 0 && data[len(data)-1] == '\r' {
return data[0 : len(data)-1]
}
return data
}
func ScanCRLF(data []byte, atEOF bool) (advance int, token []byte, err error) {
if atEOF && len(data) == 0 {
return 0, nil, nil
}
if i := bytes.Index(data, []byte{'\r', '\n'}); i >= 0 {
// We have a full newline-terminated line.
return i + 2, dropCR(data[0:i]), nil
}
// If we're at EOF, we have a final, non-terminated line. Return it.
if atEOF {
return len(data), dropCR(data), nil
}
// Request more data.
return 0, nil, nil
}