mirror of
https://github.com/EchoVault/SugarDB.git
synced 2025-10-12 19:30:35 +08:00
Implemented LPUSH, RPUSH, and LRANGE commands
This commit is contained in:
@@ -36,15 +36,19 @@ type Server struct {
|
|||||||
plugins []Plugin
|
plugins []Plugin
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) GetData(key string) interface{} {
|
func (server *Server) Lock() {
|
||||||
server.data.mu.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]
|
return server.data.data[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (server *Server) SetData(key string, value interface{}) {
|
func (server *Server) SetData(key string, value interface{}) {
|
||||||
server.data.mu.Lock()
|
|
||||||
defer server.data.mu.Unlock()
|
|
||||||
server.data.data[key] = value
|
server.data.data[key] = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,9 +2,20 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/kelvinmwinuka/memstore/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OK = "+OK\r\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server interface {
|
type Server interface {
|
||||||
|
Lock()
|
||||||
|
Unlock()
|
||||||
GetData(key string) interface{}
|
GetData(key string) interface{}
|
||||||
SetData(key string, value 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) {
|
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() {
|
func init() {
|
||||||
|
@@ -3,6 +3,8 @@ package main
|
|||||||
import "bufio"
|
import "bufio"
|
||||||
|
|
||||||
type Server interface {
|
type Server interface {
|
||||||
|
Lock()
|
||||||
|
Unlock()
|
||||||
GetData(key string) interface{}
|
GetData(key string) interface{}
|
||||||
SetData(key string, value interface{})
|
SetData(key string, value interface{})
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Server interface {
|
type Server interface {
|
||||||
|
Lock()
|
||||||
|
Unlock()
|
||||||
GetData(key string) interface{}
|
GetData(key string) interface{}
|
||||||
SetData(key string, value interface{})
|
SetData(key string, value interface{})
|
||||||
}
|
}
|
||||||
@@ -53,11 +55,13 @@ func handleGet(cmd []string, s Server, conn *bufio.Writer) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
value := s.GetData(cmd[1])
|
value := s.GetData(cmd[1])
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
switch value.(type) {
|
switch value.(type) {
|
||||||
default:
|
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:
|
case nil:
|
||||||
conn.Write([]byte("+nil\r\n\n"))
|
conn.Write([]byte("+nil\r\n\n"))
|
||||||
case string:
|
case string:
|
||||||
@@ -74,6 +78,8 @@ func handleGet(cmd []string, s Server, conn *bufio.Writer) {
|
|||||||
func handleMGet(cmd []string, s Server, conn *bufio.Writer) {
|
func handleMGet(cmd []string, s Server, conn *bufio.Writer) {
|
||||||
vals := []string{}
|
vals := []string{}
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
|
||||||
for _, key := range cmd[1:] {
|
for _, key := range cmd[1:] {
|
||||||
switch s.GetData(key).(type) {
|
switch s.GetData(key).(type) {
|
||||||
case nil:
|
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))))
|
conn.Write([]byte(fmt.Sprintf("*%d\r\n", len(vals))))
|
||||||
|
|
||||||
for _, val := range 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.Write([]byte("-Error wrong number of args for SET command\r\n\n"))
|
||||||
conn.Flush()
|
conn.Flush()
|
||||||
case x > 3:
|
case x > 3:
|
||||||
|
s.Lock()
|
||||||
s.SetData(cmd[1], strings.Join(cmd[2:], " "))
|
s.SetData(cmd[1], strings.Join(cmd[2:], " "))
|
||||||
|
s.Unlock()
|
||||||
conn.Write([]byte("+OK\r\n"))
|
conn.Write([]byte("+OK\r\n"))
|
||||||
case x == 3:
|
case x == 3:
|
||||||
val, err := strconv.ParseFloat(cmd[2], 32)
|
val, err := strconv.ParseFloat(cmd[2], 32)
|
||||||
|
|
||||||
|
s.Lock()
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.SetData(cmd[1], cmd[2])
|
s.SetData(cmd[1], cmd[2])
|
||||||
} else if !utils.IsInteger(val) {
|
} 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.SetData(cmd[1], int(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.Unlock()
|
||||||
|
|
||||||
conn.Write([]byte("+OK\r\n\n"))
|
conn.Write([]byte("+OK\r\n\n"))
|
||||||
conn.Flush()
|
conn.Flush()
|
||||||
}
|
}
|
||||||
|
@@ -9,7 +9,7 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"sync"
|
"strconv"
|
||||||
|
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
@@ -96,6 +96,21 @@ func IsInteger(n float64) bool {
|
|||||||
return math.Mod(n, 1.0) == 0
|
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) {
|
func ReadMessage(r *bufio.ReadWriter) (message string, err error) {
|
||||||
var line [][]byte
|
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
|
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