diff --git a/client/main.go b/client/main.go index 3f2d627..3b18efb 100644 --- a/client/main.go +++ b/client/main.go @@ -2,6 +2,7 @@ package main import ( "bufio" + "bytes" "crypto/tls" "crypto/x509" "encoding/json" @@ -12,6 +13,7 @@ import ( "os" "path" + "github.com/kelvinmwinuka/memstore/serialization" "gopkg.in/yaml.v3" ) @@ -108,4 +110,33 @@ func main() { } defer conn.Close() + + done := make(chan struct{}) + // connRW := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) + stdioRW := bufio.NewReadWriter(bufio.NewReader(os.Stdin), bufio.NewWriter(os.Stdout)) + + go func() { + for { + stdioRW.Write([]byte("\n> ")) + stdioRW.Flush() + + if in, err := stdioRW.ReadBytes(byte('\n')); err != nil { + stdioRW.Write([]byte(fmt.Sprintf("ERROR: %s\n", err))) + stdioRW.Flush() + } else { + in := bytes.TrimSpace(in) + + // Check for exit command + if bytes.Equal(bytes.ToLower(in), []byte("exit")) { + break + } + + stdioRW.Write(serialization.Encode(in)) + stdioRW.Flush() + } + } + done <- struct{}{} + }() + + <-done } diff --git a/serialization/decode.go b/serialization/decode.go new file mode 100644 index 0000000..957a87f --- /dev/null +++ b/serialization/decode.go @@ -0,0 +1 @@ +package serialization diff --git a/serialization/encode.go b/serialization/encode.go new file mode 100644 index 0000000..63cf0a5 --- /dev/null +++ b/serialization/encode.go @@ -0,0 +1,83 @@ +package serialization + +import ( + "bytes" + "errors" + "fmt" + "log" +) + +func tokenize(b []byte) ([][]byte, error) { + qOpen := false + transformed := []byte("") + + for _, c := range b { + + if c != ' ' && c != '"' { + transformed = append(transformed, c) + continue + } + + if c == '"' { + qOpen = !qOpen + + if qOpen && !bytes.HasSuffix(transformed, []byte(" ")) { + transformed = append(transformed, ' ') + } + + transformed = append(transformed, c) + continue + } + + if c == ' ' && qOpen { + transformed = append(transformed, []byte("*-*")...) + continue + } + + if c == ' ' && !qOpen { + transformed = append(transformed, c) + continue + } + } + + if qOpen { + return nil, errors.New("open quote in command") + } + + tokens := bytes.Split(transformed, []byte(" ")) + + for i := 0; i < len(tokens); i++ { + tokens[i] = bytes.Trim(tokens[i], "\"") + tokens[i] = bytes.ReplaceAll(tokens[i], []byte("*-*"), []byte(" ")) + } + + return tokens, nil +} + +func Encode(b []byte) []byte { + tokens, err := tokenize(b) + + if err != nil { + log.Fatal(err) + } + + if len(tokens) <= 0 { + return b + } + + if len(tokens) == 1 && bytes.Equal(bytes.ToLower(tokens[0]), []byte("ping")) { + return []byte(fmt.Sprintf("+%s\r\n", string(bytes.ToUpper(tokens[0])))) + } + + if len(tokens) > 1 && bytes.Equal(bytes.ToLower(tokens[0]), []byte("ping")) { + enc := []byte(fmt.Sprintf("*%d\r\n$%d\r\n%s\r\n", + len(tokens), len(tokens[0]), string(bytes.ToUpper(tokens[0])))) + for i := 1; i < len(tokens); i++ { + token := tokens[i] + enc = append(enc, []byte(fmt.Sprintf("$%d\r\n%s\r\n", len(token), token))...) + } + return enc + } + + return b +}