mirror of
https://github.com/EchoVault/SugarDB.git
synced 2025-10-12 03:10:04 +08:00
Implemented LPUSH, RPUSH, and LRANGE commands
This commit is contained in:
@@ -36,15 +36,19 @@ type Server struct {
|
||||
plugins []Plugin
|
||||
}
|
||||
|
||||
func (server *Server) GetData(key string) interface{} {
|
||||
func (server *Server) Lock() {
|
||||
server.data.mu.Lock()
|
||||
defer server.data.mu.Unlock()
|
||||
}
|
||||
|
||||
func (Server *Server) Unlock() {
|
||||
Server.data.mu.Unlock()
|
||||
}
|
||||
|
||||
func (server *Server) GetData(key string) interface{} {
|
||||
return server.data.data[key]
|
||||
}
|
||||
|
||||
func (server *Server) SetData(key string, value interface{}) {
|
||||
server.data.mu.Lock()
|
||||
defer server.data.mu.Unlock()
|
||||
server.data.data[key] = value
|
||||
}
|
||||
|
||||
|
@@ -2,9 +2,20 @@ package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/kelvinmwinuka/memstore/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
OK = "+OK\r\n\n"
|
||||
)
|
||||
|
||||
type Server interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
GetData(key string) interface{}
|
||||
SetData(key string, value interface{})
|
||||
}
|
||||
@@ -30,6 +41,182 @@ func (p *plugin) Description() string {
|
||||
}
|
||||
|
||||
func (p *plugin) HandleCommand(cmd []string, server interface{}, conn *bufio.Writer) {
|
||||
switch strings.ToLower(cmd[0]) {
|
||||
case "lrange":
|
||||
handleLRange(cmd, server.(Server), conn)
|
||||
case "lpush":
|
||||
handleLPush(cmd, server.(Server), conn)
|
||||
case "rpush":
|
||||
handleRPush(cmd, server.(Server), conn)
|
||||
}
|
||||
}
|
||||
|
||||
func handleLRange(cmd []string, server Server, conn *bufio.Writer) {
|
||||
if len(cmd) != 4 {
|
||||
conn.Write([]byte("-Error wrong number of arguments for LRANGE command\r\n\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
start, startOk := utils.AdaptType(cmd[2]).(int)
|
||||
end, endOk := utils.AdaptType(cmd[3]).(int)
|
||||
|
||||
if !startOk || !endOk {
|
||||
conn.Write([]byte("-Error both start and end indices must be integers\r\n\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
server.Lock()
|
||||
|
||||
list, ok := server.GetData(cmd[1]).([]interface{})
|
||||
|
||||
server.Unlock()
|
||||
|
||||
if !ok {
|
||||
conn.Write([]byte("-Error type cannot be returned with LRANGE command\r\n\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure start is within range
|
||||
if !(start >= 0 && start < len(list)) {
|
||||
conn.Write([]byte("-Error start index not within list range\r\n\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure end is within range, or is -1 otherwise
|
||||
if !((end >= 0 && end < len(list)) || end == -1) {
|
||||
conn.Write([]byte("-Error end index must be within list range or -1\r\n\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// If end is -1, read list from start to the end of the list
|
||||
if end == -1 {
|
||||
conn.Write([]byte("*" + fmt.Sprint(len(list)-start) + "\r\n"))
|
||||
for i := start; i < len(list); i++ {
|
||||
str := fmt.Sprintf("%v", list[i])
|
||||
conn.Write([]byte("$" + fmt.Sprint(len(str)) + "\r\n" + str + "\r\n"))
|
||||
}
|
||||
conn.Write([]byte("\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// Make sure start and end are not equal to each other
|
||||
if start == end {
|
||||
conn.Write([]byte("-Error start and end indices cannot be equal equal\r\n\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
// If end is not -1:
|
||||
// 1) If end is larger than start, return slice from start -> end
|
||||
// 2) If end is smaller than start, return slice from end -> start
|
||||
conn.Write([]byte("*" + fmt.Sprint(int(math.Abs(float64(start-end)))+1) + "\r\n"))
|
||||
|
||||
i := start
|
||||
j := end + 1
|
||||
if start > end {
|
||||
j = end - 1
|
||||
}
|
||||
|
||||
for i != j {
|
||||
str := fmt.Sprintf("%v", list[i])
|
||||
conn.Write([]byte("$" + fmt.Sprint(len(str)) + "\r\n" + str + "\r\n"))
|
||||
if start < end {
|
||||
i++
|
||||
} else {
|
||||
i--
|
||||
}
|
||||
|
||||
}
|
||||
conn.Write([]byte("\n"))
|
||||
conn.Flush()
|
||||
}
|
||||
|
||||
func handleLPush(cmd []string, server Server, conn *bufio.Writer) {
|
||||
if len(cmd) < 3 {
|
||||
conn.Write([]byte("-Error wrong number of arguments for LPUSH command\r\n\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
server.Lock()
|
||||
|
||||
newElems := []interface{}{}
|
||||
|
||||
for _, elem := range cmd[2:] {
|
||||
newElems = append(newElems, utils.AdaptType(elem))
|
||||
}
|
||||
|
||||
currentList := server.GetData(cmd[1])
|
||||
|
||||
if currentList == nil {
|
||||
server.SetData(cmd[1], newElems)
|
||||
server.Unlock()
|
||||
conn.Write([]byte(OK))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
l, ok := currentList.([]interface{})
|
||||
|
||||
if !ok {
|
||||
server.Unlock()
|
||||
conn.Write([]byte("-Error LPUSH command on non-list item\r\n\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
server.SetData(cmd[1], append(newElems, l...))
|
||||
server.Unlock()
|
||||
|
||||
conn.Write([]byte(OK))
|
||||
conn.Flush()
|
||||
}
|
||||
|
||||
func handleRPush(cmd []string, server Server, conn *bufio.Writer) {
|
||||
if len(cmd) < 3 {
|
||||
conn.Write([]byte("-Error wrong number of arguments for LPUSH command\r\n\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
server.Lock()
|
||||
|
||||
newElems := []interface{}{}
|
||||
|
||||
for _, elem := range cmd[2:] {
|
||||
newElems = append(newElems, utils.AdaptType(elem))
|
||||
}
|
||||
|
||||
currentList := server.GetData(cmd[1])
|
||||
|
||||
if currentList == nil {
|
||||
server.SetData(cmd[1], newElems)
|
||||
server.Unlock()
|
||||
conn.Write([]byte(OK))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
l, ok := currentList.([]interface{})
|
||||
|
||||
if !ok {
|
||||
server.Unlock()
|
||||
conn.Write([]byte("-Error RPUSH command on non-list item\r\n\n"))
|
||||
conn.Flush()
|
||||
return
|
||||
}
|
||||
|
||||
server.SetData(cmd[1], append(l, newElems...))
|
||||
server.Unlock()
|
||||
|
||||
conn.Write([]byte(OK))
|
||||
conn.Flush()
|
||||
}
|
||||
|
||||
func init() {
|
||||
|
@@ -3,6 +3,8 @@ package main
|
||||
import "bufio"
|
||||
|
||||
type Server interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
GetData(key string) interface{}
|
||||
SetData(key string, value interface{})
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@ import (
|
||||
)
|
||||
|
||||
type Server interface {
|
||||
Lock()
|
||||
Unlock()
|
||||
GetData(key string) interface{}
|
||||
SetData(key string, value interface{})
|
||||
}
|
||||
@@ -53,11 +55,13 @@ func handleGet(cmd []string, s Server, conn *bufio.Writer) {
|
||||
return
|
||||
}
|
||||
|
||||
s.Lock()
|
||||
value := s.GetData(cmd[1])
|
||||
s.Unlock()
|
||||
|
||||
switch value.(type) {
|
||||
default:
|
||||
fmt.Println("Error. The requested object's type cannot be returned with the GET command")
|
||||
conn.Write([]byte("-Error type cannot be returned with the GET command\r\n\n"))
|
||||
case nil:
|
||||
conn.Write([]byte("+nil\r\n\n"))
|
||||
case string:
|
||||
@@ -74,6 +78,8 @@ func handleGet(cmd []string, s Server, conn *bufio.Writer) {
|
||||
func handleMGet(cmd []string, s Server, conn *bufio.Writer) {
|
||||
vals := []string{}
|
||||
|
||||
s.Lock()
|
||||
|
||||
for _, key := range cmd[1:] {
|
||||
switch s.GetData(key).(type) {
|
||||
case nil:
|
||||
@@ -87,6 +93,8 @@ func handleMGet(cmd []string, s Server, conn *bufio.Writer) {
|
||||
}
|
||||
}
|
||||
|
||||
s.Unlock()
|
||||
|
||||
conn.Write([]byte(fmt.Sprintf("*%d\r\n", len(vals))))
|
||||
|
||||
for _, val := range vals {
|
||||
@@ -103,11 +111,15 @@ func handleSet(cmd []string, s Server, conn *bufio.Writer) {
|
||||
conn.Write([]byte("-Error wrong number of args for SET command\r\n\n"))
|
||||
conn.Flush()
|
||||
case x > 3:
|
||||
s.Lock()
|
||||
s.SetData(cmd[1], strings.Join(cmd[2:], " "))
|
||||
s.Unlock()
|
||||
conn.Write([]byte("+OK\r\n"))
|
||||
case x == 3:
|
||||
val, err := strconv.ParseFloat(cmd[2], 32)
|
||||
|
||||
s.Lock()
|
||||
|
||||
if err != nil {
|
||||
s.SetData(cmd[1], cmd[2])
|
||||
} else if !utils.IsInteger(val) {
|
||||
@@ -116,6 +128,8 @@ func handleSet(cmd []string, s Server, conn *bufio.Writer) {
|
||||
s.SetData(cmd[1], int(val))
|
||||
}
|
||||
|
||||
s.Unlock()
|
||||
|
||||
conn.Write([]byte("+OK\r\n\n"))
|
||||
conn.Flush()
|
||||
}
|
||||
|
@@ -9,7 +9,7 @@ import (
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"sync"
|
||||
"strconv"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
@@ -96,6 +96,21 @@ func IsInteger(n float64) bool {
|
||||
return math.Mod(n, 1.0) == 0
|
||||
}
|
||||
|
||||
func AdaptType(s string) interface{} {
|
||||
// Adapt the type of the parameter to string, float64 or int
|
||||
n, err := strconv.ParseFloat(s, 32)
|
||||
|
||||
if err != nil {
|
||||
return s
|
||||
}
|
||||
|
||||
if IsInteger(n) {
|
||||
return int(n)
|
||||
}
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
func ReadMessage(r *bufio.ReadWriter) (message string, err error) {
|
||||
var line [][]byte
|
||||
|
||||
@@ -116,20 +131,3 @@ func ReadMessage(r *bufio.ReadWriter) (message string, err error) {
|
||||
|
||||
return fmt.Sprintf("%s\r\n", string(bytes.Join(line, []byte("\r\n")))), nil
|
||||
}
|
||||
|
||||
type Plugin interface {
|
||||
Name() string
|
||||
Commands() []string
|
||||
Description() string
|
||||
HandleCommand(
|
||||
cmd []string,
|
||||
GetData *func(key string) interface{},
|
||||
SetData *func(key string, value interface{}),
|
||||
conn *bufio.Writer,
|
||||
)
|
||||
}
|
||||
|
||||
type Data struct {
|
||||
mu sync.Mutex
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
Reference in New Issue
Block a user