mirror of
https://github.com/EchoVault/SugarDB.git
synced 2025-10-10 10:20:08 +08:00
Moved get, list, ping, set, and string plugins into internal modules folder. This should simplify future testing efforts.
This commit is contained in:
6
Makefile
6
Makefile
@@ -1,11 +1,5 @@
|
|||||||
build-plugins:
|
build-plugins:
|
||||||
CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildmode=plugin -o $(DEST)/command_ping.so ./src/plugins/ping/*.go
|
|
||||||
CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildmode=plugin -o $(DEST)/command_set.so ./src/plugins/set/*.go
|
|
||||||
CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildmode=plugin -o $(DEST)/command_get.so ./src/plugins/get/*.go
|
|
||||||
CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildmode=plugin -o $(DEST)/command_list.so ./src/plugins/list/*.go
|
|
||||||
CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildmode=plugin -o $(DEST)/command_pubsub.so ./src/plugins/pubsub/*.go
|
CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildmode=plugin -o $(DEST)/command_pubsub.so ./src/plugins/pubsub/*.go
|
||||||
CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) GOOS=$(GOOS) GOARCH=$(GOARCH) go build -buildmode=plugin -o $(DEST)/command_string.so ./src/plugins/string/*.go
|
|
||||||
|
|
||||||
|
|
||||||
build-server:
|
build-server:
|
||||||
CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $(DEST)/server ./src/*.go
|
CGO_ENABLED=$(CGO_ENABLED) CC=$(CC) GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $(DEST)/server ./src/*.go
|
||||||
|
22
src/main.go
22
src/main.go
@@ -6,7 +6,12 @@ import (
|
|||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/kelvinmwinuka/memstore/src/acl"
|
"github.com/kelvinmwinuka/memstore/src/modules/acl"
|
||||||
|
"github.com/kelvinmwinuka/memstore/src/modules/get"
|
||||||
|
"github.com/kelvinmwinuka/memstore/src/modules/list"
|
||||||
|
"github.com/kelvinmwinuka/memstore/src/modules/ping"
|
||||||
|
"github.com/kelvinmwinuka/memstore/src/modules/set"
|
||||||
|
str "github.com/kelvinmwinuka/memstore/src/modules/string"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
@@ -131,6 +136,7 @@ func (server *Server) handlePluginCommand(ctx context.Context, cmd []string, con
|
|||||||
if command.HandleWithConnection {
|
if command.HandleWithConnection {
|
||||||
return command.Plugin.HandleCommandWithConnection(ctx, cmd, server, conn)
|
return command.Plugin.HandleCommandWithConnection(ctx, cmd, server, conn)
|
||||||
}
|
}
|
||||||
|
fmt.Println("SERVER: ", server)
|
||||||
return command.Plugin.HandleCommand(ctx, cmd, server)
|
return command.Plugin.HandleCommand(ctx, cmd, server)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -303,8 +309,18 @@ func (server *Server) StartHTTP(ctx context.Context) {
|
|||||||
func (server *Server) LoadPlugins(ctx context.Context) {
|
func (server *Server) LoadPlugins(ctx context.Context) {
|
||||||
conf := server.config
|
conf := server.config
|
||||||
|
|
||||||
// Load ACL Internal Commands
|
// Load Ping module
|
||||||
server.commands = append(server.commands, server.ACL.GetPluginCommands()...)
|
server.commands = append(server.commands, ping.NewModule().GetCommands()...)
|
||||||
|
// Load ACL Commands module
|
||||||
|
server.commands = append(server.commands, acl.NewModule(server.ACL).GetCommands()...)
|
||||||
|
// Load Set module
|
||||||
|
server.commands = append(server.commands, set.NewModule().GetCommands()...)
|
||||||
|
// Load String module
|
||||||
|
server.commands = append(server.commands, str.NewModule().GetCommands()...)
|
||||||
|
// Load Get module
|
||||||
|
server.commands = append(server.commands, get.NewModule().GetCommands()...)
|
||||||
|
// Load List module
|
||||||
|
server.commands = append(server.commands, list.NewModule().GetCommands()...)
|
||||||
|
|
||||||
// Load plugins /usr/local/lib/memstore
|
// Load plugins /usr/local/lib/memstore
|
||||||
files, err := os.ReadDir(conf.PluginDir)
|
files, err := os.ReadDir(conf.PluginDir)
|
||||||
|
@@ -42,7 +42,6 @@ type ACL struct {
|
|||||||
Users []User
|
Users []User
|
||||||
Connections map[*net.Conn]*User
|
Connections map[*net.Conn]*User
|
||||||
Config utils.Config
|
Config utils.Config
|
||||||
Plugin Plugin
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewACL(config utils.Config) *ACL {
|
func NewACL(config utils.Config) *ACL {
|
||||||
@@ -113,17 +112,12 @@ func NewACL(config utils.Config) *ACL {
|
|||||||
Connections: make(map[*net.Conn]*User),
|
Connections: make(map[*net.Conn]*User),
|
||||||
Config: config,
|
Config: config,
|
||||||
}
|
}
|
||||||
acl.Plugin = NewACLPlugin(&acl)
|
|
||||||
|
|
||||||
fmt.Println(acl.Users)
|
fmt.Println(acl.Users)
|
||||||
|
|
||||||
return &acl
|
return &acl
|
||||||
}
|
}
|
||||||
|
|
||||||
func (acl *ACL) GetPluginCommands() []utils.Command {
|
|
||||||
return acl.Plugin.GetCommands()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (acl *ACL) RegisterConnection(conn *net.Conn) {
|
func (acl *ACL) RegisterConnection(conn *net.Conn) {
|
||||||
fmt.Println("Register connection...")
|
fmt.Println("Register connection...")
|
||||||
}
|
}
|
@@ -17,6 +17,8 @@ type Plugin struct {
|
|||||||
acl *ACL
|
acl *ACL
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var ACLPlugin Plugin
|
||||||
|
|
||||||
func (p Plugin) Name() string {
|
func (p Plugin) Name() string {
|
||||||
return p.name
|
return p.name
|
||||||
}
|
}
|
||||||
@@ -25,6 +27,10 @@ func (p Plugin) Commands() ([]byte, error) {
|
|||||||
return json.Marshal(p.commands)
|
return json.Marshal(p.commands)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p Plugin) GetCommands() []utils.Command {
|
||||||
|
return p.commands
|
||||||
|
}
|
||||||
|
|
||||||
func (p Plugin) Description() string {
|
func (p Plugin) Description() string {
|
||||||
return p.description
|
return p.description
|
||||||
}
|
}
|
||||||
@@ -72,10 +78,6 @@ func (p Plugin) HandleCommand(ctx context.Context, cmd []string, server utils.Se
|
|||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p Plugin) GetCommands() []utils.Command {
|
|
||||||
return p.commands
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p Plugin) handleAuth(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
func (p Plugin) handleAuth(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
||||||
return nil, errors.New("AUTH not implemented")
|
return nil, errors.New("AUTH not implemented")
|
||||||
}
|
}
|
||||||
@@ -120,9 +122,7 @@ func (p Plugin) handleSave(ctx context.Context, cmd []string, server utils.Serve
|
|||||||
return nil, errors.New("ACL SAVE not implemented")
|
return nil, errors.New("ACL SAVE not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
var ACLPlugin Plugin
|
func NewModule(acl *ACL) Plugin {
|
||||||
|
|
||||||
func NewACLPlugin(acl *ACL) Plugin {
|
|
||||||
ACLPlugin = Plugin{
|
ACLPlugin = Plugin{
|
||||||
acl: acl,
|
acl: acl,
|
||||||
name: "ACLCommands",
|
name: "ACLCommands",
|
128
src/modules/get/commands.go
Normal file
128
src/modules/get/commands.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package get
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/kelvinmwinuka/memstore/src/utils"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Plugin struct {
|
||||||
|
name string
|
||||||
|
commands []utils.Command
|
||||||
|
categories []string
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
var GetModule Plugin
|
||||||
|
|
||||||
|
func (p Plugin) Name() string {
|
||||||
|
return p.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) Commands() ([]byte, error) {
|
||||||
|
return json.Marshal(p.commands)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) GetCommands() []utils.Command {
|
||||||
|
return p.commands
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) Description() string {
|
||||||
|
return p.description
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) HandleCommandWithConnection(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
||||||
|
return nil, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) HandleCommand(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
|
switch strings.ToLower(cmd[0]) {
|
||||||
|
default:
|
||||||
|
return nil, errors.New("command unknown")
|
||||||
|
case "get":
|
||||||
|
return handleGet(ctx, cmd, server)
|
||||||
|
case "mget":
|
||||||
|
return handleMGet(ctx, cmd, server)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleGet(ctx context.Context, cmd []string, s utils.Server) ([]byte, error) {
|
||||||
|
if len(cmd) != 2 {
|
||||||
|
return nil, errors.New("wrong number of args for GET command")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := cmd[1]
|
||||||
|
|
||||||
|
s.KeyRLock(ctx, key)
|
||||||
|
value := s.GetValue(key)
|
||||||
|
s.KeyRUnlock(key)
|
||||||
|
|
||||||
|
switch value.(type) {
|
||||||
|
default:
|
||||||
|
return []byte(fmt.Sprintf("+%v\r\n\n", value)), nil
|
||||||
|
case nil:
|
||||||
|
return []byte("+nil\r\n\n"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMGet(ctx context.Context, cmd []string, s utils.Server) ([]byte, error) {
|
||||||
|
if len(cmd) < 2 {
|
||||||
|
return nil, errors.New("wrong number of args for MGET command")
|
||||||
|
}
|
||||||
|
|
||||||
|
vals := []string{}
|
||||||
|
|
||||||
|
for _, key := range cmd[1:] {
|
||||||
|
func(key string) {
|
||||||
|
s.KeyRLock(ctx, key)
|
||||||
|
switch s.GetValue(key).(type) {
|
||||||
|
default:
|
||||||
|
vals = append(vals, fmt.Sprintf("%v", s.GetValue(key)))
|
||||||
|
case nil:
|
||||||
|
vals = append(vals, "nil")
|
||||||
|
}
|
||||||
|
s.KeyRUnlock(key)
|
||||||
|
|
||||||
|
}(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes := []byte(fmt.Sprintf("*%d\r\n", len(vals)))
|
||||||
|
|
||||||
|
for _, val := range vals {
|
||||||
|
bytes = append(bytes, []byte(fmt.Sprintf("$%d\r\n%s\r\n", len(val), val))...)
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = append(bytes, []byte("\n")...)
|
||||||
|
|
||||||
|
return bytes, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewModule() Plugin {
|
||||||
|
GetModule := Plugin{
|
||||||
|
name: "GetCommands",
|
||||||
|
commands: []utils.Command{
|
||||||
|
{
|
||||||
|
Command: "get",
|
||||||
|
Categories: []string{},
|
||||||
|
Description: "",
|
||||||
|
HandleWithConnection: false,
|
||||||
|
Sync: false,
|
||||||
|
Plugin: GetModule,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: "mget",
|
||||||
|
Categories: []string{},
|
||||||
|
Description: "",
|
||||||
|
HandleWithConnection: false,
|
||||||
|
Sync: true,
|
||||||
|
Plugin: GetModule,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Handle basic GET and MGET commands",
|
||||||
|
}
|
||||||
|
return GetModule
|
||||||
|
}
|
@@ -1,10 +1,11 @@
|
|||||||
package main
|
package list
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/kelvinmwinuka/memstore/src/utils"
|
||||||
"math"
|
"math"
|
||||||
"net"
|
"net"
|
||||||
"reflect"
|
"reflect"
|
||||||
@@ -15,89 +16,74 @@ const (
|
|||||||
OK = "+OK\r\n\n"
|
OK = "+OK\r\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server interface {
|
type Plugin struct {
|
||||||
KeyLock(ctx context.Context, key string) (bool, error)
|
|
||||||
KeyUnlock(key string)
|
|
||||||
KeyRLock(ctx context.Context, key string) (bool, error)
|
|
||||||
KeyRUnlock(key string)
|
|
||||||
KeyExists(key string) bool
|
|
||||||
CreateKeyAndLock(ctx context.Context, key string) (bool, error)
|
|
||||||
GetValue(key string) interface{}
|
|
||||||
SetValue(ctx context.Context, key string, value interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Command struct {
|
|
||||||
Command string `json:"Command"`
|
|
||||||
Categories []string `json:"Categories"`
|
|
||||||
Description string `json:"Description"`
|
|
||||||
HandleWithConnection bool `json:"HandleWithConnection"`
|
|
||||||
Sync bool `json:"Sync"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type plugin struct {
|
|
||||||
name string
|
name string
|
||||||
commands []Command
|
commands []utils.Command
|
||||||
categories []string
|
categories []string
|
||||||
description string
|
description string
|
||||||
}
|
}
|
||||||
|
|
||||||
var Plugin plugin
|
var ListModule Plugin
|
||||||
|
|
||||||
func (p *plugin) Name() string {
|
func (p Plugin) Name() string {
|
||||||
return p.name
|
return p.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) Commands() ([]byte, error) {
|
func (p Plugin) Commands() ([]byte, error) {
|
||||||
return json.Marshal(p.commands)
|
return json.Marshal(p.commands)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) Description() string {
|
func (p Plugin) GetCommands() []utils.Command {
|
||||||
|
return p.commands
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) Description() string {
|
||||||
return p.description
|
return p.description
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) HandleCommandWithConnection(ctx context.Context, cmd []string, server interface{}, conn *net.Conn) ([]byte, error) {
|
func (p Plugin) HandleCommandWithConnection(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) HandleCommand(ctx context.Context, cmd []string, server interface{}) ([]byte, error) {
|
func (p Plugin) HandleCommand(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
c := strings.ToLower(cmd[0])
|
c := strings.ToLower(cmd[0])
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("command unknown")
|
return nil, errors.New("command unknown")
|
||||||
case c == "llen":
|
case c == "llen":
|
||||||
return handleLLen(ctx, cmd, server.(Server))
|
return handleLLen(ctx, cmd, server)
|
||||||
|
|
||||||
case c == "lindex":
|
case c == "lindex":
|
||||||
return handleLIndex(ctx, cmd, server.(Server))
|
return handleLIndex(ctx, cmd, server)
|
||||||
|
|
||||||
case c == "lrange":
|
case c == "lrange":
|
||||||
return handleLRange(ctx, cmd, server.(Server))
|
return handleLRange(ctx, cmd, server)
|
||||||
|
|
||||||
case c == "lset":
|
case c == "lset":
|
||||||
return handleLSet(ctx, cmd, server.(Server))
|
return handleLSet(ctx, cmd, server)
|
||||||
|
|
||||||
case c == "ltrim":
|
case c == "ltrim":
|
||||||
return handleLTrim(ctx, cmd, server.(Server))
|
return handleLTrim(ctx, cmd, server)
|
||||||
|
|
||||||
case c == "lrem":
|
case c == "lrem":
|
||||||
return handleLRem(ctx, cmd, server.(Server))
|
return handleLRem(ctx, cmd, server)
|
||||||
|
|
||||||
case c == "lmove":
|
case c == "lmove":
|
||||||
return handleLMove(ctx, cmd, server.(Server))
|
return handleLMove(ctx, cmd, server)
|
||||||
|
|
||||||
case Contains[string]([]string{"lpush", "lpushx"}, c):
|
case utils.Contains[string]([]string{"lpush", "lpushx"}, c):
|
||||||
return handleLPush(ctx, cmd, server.(Server))
|
return handleLPush(ctx, cmd, server)
|
||||||
|
|
||||||
case Contains[string]([]string{"rpush", "rpushx"}, c):
|
case utils.Contains[string]([]string{"rpush", "rpushx"}, c):
|
||||||
return handleRPush(ctx, cmd, server.(Server))
|
return handleRPush(ctx, cmd, server)
|
||||||
|
|
||||||
case Contains[string]([]string{"lpop", "rpop"}, c):
|
case utils.Contains[string]([]string{"lpop", "rpop"}, c):
|
||||||
return handlePop(ctx, cmd, server.(Server))
|
return handlePop(ctx, cmd, server)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLLen(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleLLen(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd) != 2 {
|
if len(cmd) != 2 {
|
||||||
return nil, errors.New("wrong number of args for LLEN command")
|
return nil, errors.New("wrong number of args for LLEN command")
|
||||||
}
|
}
|
||||||
@@ -118,12 +104,12 @@ func handleLLen(ctx context.Context, cmd []string, server Server) ([]byte, error
|
|||||||
return []byte(fmt.Sprintf(":%d\r\n\n", len(list))), nil
|
return []byte(fmt.Sprintf(":%d\r\n\n", len(list))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLIndex(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleLIndex(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd) != 3 {
|
if len(cmd) != 3 {
|
||||||
return nil, errors.New("wrong number of args for LINDEX command")
|
return nil, errors.New("wrong number of args for LINDEX command")
|
||||||
}
|
}
|
||||||
|
|
||||||
index, ok := AdaptType(cmd[2]).(int64)
|
index, ok := utils.AdaptType(cmd[2]).(int64)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("index must be an integer")
|
return nil, errors.New("index must be an integer")
|
||||||
@@ -148,13 +134,13 @@ func handleLIndex(ctx context.Context, cmd []string, server Server) ([]byte, err
|
|||||||
return []byte(fmt.Sprintf("+%s\r\n\n", list[index])), nil
|
return []byte(fmt.Sprintf("+%s\r\n\n", list[index])), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLRange(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleLRange(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd) != 4 {
|
if len(cmd) != 4 {
|
||||||
return nil, errors.New("wrong number of arguments for LRANGE command")
|
return nil, errors.New("wrong number of arguments for LRANGE command")
|
||||||
}
|
}
|
||||||
|
|
||||||
start, startOk := AdaptType(cmd[2]).(int64)
|
start, startOk := utils.AdaptType(cmd[2]).(int64)
|
||||||
end, endOk := AdaptType(cmd[3]).(int64)
|
end, endOk := utils.AdaptType(cmd[3]).(int64)
|
||||||
|
|
||||||
if !startOk || !endOk {
|
if !startOk || !endOk {
|
||||||
return nil, errors.New("both start and end indices must be integers")
|
return nil, errors.New("both start and end indices must be integers")
|
||||||
@@ -227,7 +213,7 @@ func handleLRange(ctx context.Context, cmd []string, server Server) ([]byte, err
|
|||||||
return bytes, nil
|
return bytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLSet(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleLSet(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd) != 4 {
|
if len(cmd) != 4 {
|
||||||
return nil, errors.New("wrong number of arguments for LSET command")
|
return nil, errors.New("wrong number of arguments for LSET command")
|
||||||
}
|
}
|
||||||
@@ -244,7 +230,7 @@ func handleLSet(ctx context.Context, cmd []string, server Server) ([]byte, error
|
|||||||
return nil, errors.New("LSET command on non-list item")
|
return nil, errors.New("LSET command on non-list item")
|
||||||
}
|
}
|
||||||
|
|
||||||
index, ok := AdaptType(cmd[2]).(int64)
|
index, ok := utils.AdaptType(cmd[2]).(int64)
|
||||||
|
|
||||||
fmt.Printf("LSET INDEX: `%v`, OK: %v\n", reflect.TypeOf(index), ok)
|
fmt.Printf("LSET INDEX: `%v`, OK: %v\n", reflect.TypeOf(index), ok)
|
||||||
|
|
||||||
@@ -258,20 +244,20 @@ func handleLSet(ctx context.Context, cmd []string, server Server) ([]byte, error
|
|||||||
return nil, errors.New("index must be within range")
|
return nil, errors.New("index must be within range")
|
||||||
}
|
}
|
||||||
|
|
||||||
list[index] = AdaptType(cmd[3])
|
list[index] = utils.AdaptType(cmd[3])
|
||||||
server.SetValue(ctx, cmd[1], list)
|
server.SetValue(ctx, cmd[1], list)
|
||||||
server.KeyUnlock(cmd[1])
|
server.KeyUnlock(cmd[1])
|
||||||
|
|
||||||
return []byte(OK), nil
|
return []byte(OK), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLTrim(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleLTrim(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd) != 4 {
|
if len(cmd) != 4 {
|
||||||
return nil, errors.New("wrong number of args for command LTRIM")
|
return nil, errors.New("wrong number of args for command LTRIM")
|
||||||
}
|
}
|
||||||
|
|
||||||
start, startOk := AdaptType(cmd[2]).(int64)
|
start, startOk := utils.AdaptType(cmd[2]).(int64)
|
||||||
end, endOk := AdaptType(cmd[3]).(int64)
|
end, endOk := utils.AdaptType(cmd[3]).(int64)
|
||||||
|
|
||||||
if !startOk || !endOk {
|
if !startOk || !endOk {
|
||||||
return nil, errors.New("start and end indices must be integers")
|
return nil, errors.New("start and end indices must be integers")
|
||||||
@@ -307,13 +293,13 @@ func handleLTrim(ctx context.Context, cmd []string, server Server) ([]byte, erro
|
|||||||
return []byte(OK), nil
|
return []byte(OK), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLRem(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleLRem(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd) != 4 {
|
if len(cmd) != 4 {
|
||||||
return nil, errors.New("wrong number of arguments for LREM command")
|
return nil, errors.New("wrong number of arguments for LREM command")
|
||||||
}
|
}
|
||||||
|
|
||||||
value := cmd[3]
|
value := cmd[3]
|
||||||
count, ok := AdaptType(cmd[2]).(int64)
|
count, ok := utils.AdaptType(cmd[2]).(int64)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("count must be an integer")
|
return nil, errors.New("count must be an integer")
|
||||||
@@ -359,7 +345,7 @@ func handleLRem(ctx context.Context, cmd []string, server Server) ([]byte, error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list = Filter[interface{}](list, func(elem interface{}) bool {
|
list = utils.Filter[interface{}](list, func(elem interface{}) bool {
|
||||||
return elem != nil
|
return elem != nil
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -369,7 +355,7 @@ func handleLRem(ctx context.Context, cmd []string, server Server) ([]byte, error
|
|||||||
return []byte(OK), nil
|
return []byte(OK), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLMove(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleLMove(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd) != 5 {
|
if len(cmd) != 5 {
|
||||||
return nil, errors.New("wrong number of arguments for LMOVE command")
|
return nil, errors.New("wrong number of arguments for LMOVE command")
|
||||||
}
|
}
|
||||||
@@ -377,7 +363,7 @@ func handleLMove(ctx context.Context, cmd []string, server Server) ([]byte, erro
|
|||||||
whereFrom := strings.ToLower(cmd[3])
|
whereFrom := strings.ToLower(cmd[3])
|
||||||
whereTo := strings.ToLower(cmd[4])
|
whereTo := strings.ToLower(cmd[4])
|
||||||
|
|
||||||
if !Contains[string]([]string{"left", "right"}, whereFrom) || !Contains[string]([]string{"left", "right"}, whereTo) {
|
if !utils.Contains[string]([]string{"left", "right"}, whereFrom) || !utils.Contains[string]([]string{"left", "right"}, whereTo) {
|
||||||
return nil, errors.New("wherefrom and whereto arguments must be either LEFT or RIGHT")
|
return nil, errors.New("wherefrom and whereto arguments must be either LEFT or RIGHT")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,7 +404,7 @@ func handleLMove(ctx context.Context, cmd []string, server Server) ([]byte, erro
|
|||||||
return []byte(OK), nil
|
return []byte(OK), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleLPush(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleLPush(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd) < 3 {
|
if len(cmd) < 3 {
|
||||||
return nil, fmt.Errorf("wrong number of arguments for %s command", strings.ToUpper(cmd[0]))
|
return nil, fmt.Errorf("wrong number of arguments for %s command", strings.ToUpper(cmd[0]))
|
||||||
}
|
}
|
||||||
@@ -426,7 +412,7 @@ func handleLPush(ctx context.Context, cmd []string, server Server) ([]byte, erro
|
|||||||
newElems := []interface{}{}
|
newElems := []interface{}{}
|
||||||
|
|
||||||
for _, elem := range cmd[2:] {
|
for _, elem := range cmd[2:] {
|
||||||
newElems = append(newElems, AdaptType(elem))
|
newElems = append(newElems, utils.AdaptType(elem))
|
||||||
}
|
}
|
||||||
|
|
||||||
key := cmd[1]
|
key := cmd[1]
|
||||||
@@ -456,7 +442,7 @@ func handleLPush(ctx context.Context, cmd []string, server Server) ([]byte, erro
|
|||||||
return []byte(OK), nil
|
return []byte(OK), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleRPush(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleRPush(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd) < 3 {
|
if len(cmd) < 3 {
|
||||||
return nil, fmt.Errorf("wrong number of arguments for %s command", strings.ToUpper(cmd[0]))
|
return nil, fmt.Errorf("wrong number of arguments for %s command", strings.ToUpper(cmd[0]))
|
||||||
}
|
}
|
||||||
@@ -464,7 +450,7 @@ func handleRPush(ctx context.Context, cmd []string, server Server) ([]byte, erro
|
|||||||
newElems := []interface{}{}
|
newElems := []interface{}{}
|
||||||
|
|
||||||
for _, elem := range cmd[2:] {
|
for _, elem := range cmd[2:] {
|
||||||
newElems = append(newElems, AdaptType(elem))
|
newElems = append(newElems, utils.AdaptType(elem))
|
||||||
}
|
}
|
||||||
|
|
||||||
if !server.KeyExists(cmd[1]) {
|
if !server.KeyExists(cmd[1]) {
|
||||||
@@ -492,7 +478,7 @@ func handleRPush(ctx context.Context, cmd []string, server Server) ([]byte, erro
|
|||||||
return []byte(OK), nil
|
return []byte(OK), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handlePop(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handlePop(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd) != 2 {
|
if len(cmd) != 2 {
|
||||||
return nil, fmt.Errorf("wrong number of args for %s command", strings.ToUpper(cmd[0]))
|
return nil, fmt.Errorf("wrong number of args for %s command", strings.ToUpper(cmd[0]))
|
||||||
}
|
}
|
||||||
@@ -525,100 +511,116 @@ func handlePop(ctx context.Context, cmd []string, server Server) ([]byte, error)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func NewModule() Plugin {
|
||||||
Plugin.name = "ListCommands"
|
ListModule := Plugin{
|
||||||
Plugin.commands = []Command{
|
name: "ListCommands",
|
||||||
{
|
commands: []utils.Command{
|
||||||
Command: "lpush",
|
{
|
||||||
Categories: []string{},
|
Command: "lpush",
|
||||||
Description: "(LPUSH key value1 [value2]) Prepends one or more values to the beginning of a list, creates the list if it does not exist.",
|
Categories: []string{},
|
||||||
HandleWithConnection: false,
|
Description: "(LPUSH key value1 [value2]) Prepends one or more values to the beginning of a list, creates the list if it does not exist.",
|
||||||
Sync: true,
|
HandleWithConnection: false,
|
||||||
},
|
Sync: true,
|
||||||
{
|
Plugin: ListModule,
|
||||||
Command: "lpushx",
|
},
|
||||||
Categories: []string{},
|
{
|
||||||
Description: "(LPUSHX key value) Prepends a value to the beginning of a list only if the list exists.",
|
Command: "lpushx",
|
||||||
HandleWithConnection: false,
|
Categories: []string{},
|
||||||
Sync: true,
|
Description: "(LPUSHX key value) Prepends a value to the beginning of a list only if the list exists.",
|
||||||
},
|
HandleWithConnection: false,
|
||||||
{
|
Sync: true,
|
||||||
Command: "lpop",
|
Plugin: ListModule,
|
||||||
Categories: []string{},
|
},
|
||||||
Description: "(LPOP key) Removes and returns the first element of a list.",
|
{
|
||||||
HandleWithConnection: false,
|
Command: "lpop",
|
||||||
Sync: true,
|
Categories: []string{},
|
||||||
},
|
Description: "(LPOP key) Removes and returns the first element of a list.",
|
||||||
{
|
HandleWithConnection: false,
|
||||||
Command: "llen",
|
Sync: true,
|
||||||
Categories: []string{},
|
Plugin: ListModule,
|
||||||
Description: "(LLEN key) Return the length of a list.",
|
},
|
||||||
HandleWithConnection: false,
|
{
|
||||||
Sync: true,
|
Command: "llen",
|
||||||
},
|
Categories: []string{},
|
||||||
{
|
Description: "(LLEN key) Return the length of a list.",
|
||||||
Command: "lrange",
|
HandleWithConnection: false,
|
||||||
Categories: []string{},
|
Sync: true,
|
||||||
Description: "(LRANGE key start end) Return a range of elements between the given indices.",
|
Plugin: ListModule,
|
||||||
HandleWithConnection: false,
|
},
|
||||||
Sync: true,
|
{
|
||||||
},
|
Command: "lrange",
|
||||||
{
|
Categories: []string{},
|
||||||
Command: "lindex",
|
Description: "(LRANGE key start end) Return a range of elements between the given indices.",
|
||||||
Categories: []string{},
|
HandleWithConnection: false,
|
||||||
Description: "(LINDEX key index) Gets list element by index.",
|
Sync: true,
|
||||||
HandleWithConnection: false,
|
Plugin: ListModule,
|
||||||
Sync: true,
|
},
|
||||||
},
|
{
|
||||||
{
|
Command: "lindex",
|
||||||
Command: "lset",
|
Categories: []string{},
|
||||||
Categories: []string{},
|
Description: "(LINDEX key index) Gets list element by index.",
|
||||||
Description: "(LSET key index value) Sets the value of an element in a list by its index.",
|
HandleWithConnection: false,
|
||||||
HandleWithConnection: false,
|
Sync: true,
|
||||||
Sync: true,
|
Plugin: ListModule,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Command: "ltrim",
|
Command: "lset",
|
||||||
Categories: []string{},
|
Categories: []string{},
|
||||||
Description: "(LTRIM key start end) Trims a list to the specified range.",
|
Description: "(LSET key index value) Sets the value of an element in a list by its index.",
|
||||||
HandleWithConnection: false,
|
HandleWithConnection: false,
|
||||||
Sync: true,
|
Sync: true,
|
||||||
},
|
Plugin: ListModule,
|
||||||
{
|
},
|
||||||
Command: "lrem",
|
{
|
||||||
Categories: []string{},
|
Command: "ltrim",
|
||||||
Description: "(LREM key count value) Remove elements from list.",
|
Categories: []string{},
|
||||||
HandleWithConnection: false,
|
Description: "(LTRIM key start end) Trims a list to the specified range.",
|
||||||
Sync: true,
|
HandleWithConnection: false,
|
||||||
},
|
Sync: true,
|
||||||
{
|
Plugin: ListModule,
|
||||||
Command: "lmove",
|
},
|
||||||
Categories: []string{},
|
{
|
||||||
Description: "(LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> Move element from one list to the other specifying left/right for both lists.",
|
Command: "lrem",
|
||||||
HandleWithConnection: false,
|
Categories: []string{},
|
||||||
Sync: true,
|
Description: "(LREM key count value) Remove elements from list.",
|
||||||
},
|
HandleWithConnection: false,
|
||||||
{
|
Sync: true,
|
||||||
Command: "rpop",
|
Plugin: ListModule,
|
||||||
Categories: []string{},
|
},
|
||||||
Description: "(RPOP key) Removes and gets the last element in a list.",
|
{
|
||||||
HandleWithConnection: false,
|
Command: "lmove",
|
||||||
Sync: true,
|
Categories: []string{},
|
||||||
},
|
Description: "(LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT> Move element from one list to the other specifying left/right for both lists.",
|
||||||
{
|
HandleWithConnection: false,
|
||||||
Command: "rpush",
|
Sync: true,
|
||||||
Categories: []string{},
|
Plugin: ListModule,
|
||||||
Description: "(RPUSH key value [value2]) Appends one or multiple elements to the end of a list.",
|
},
|
||||||
HandleWithConnection: false,
|
{
|
||||||
Sync: true,
|
Command: "rpop",
|
||||||
},
|
Categories: []string{},
|
||||||
{
|
Description: "(RPOP key) Removes and gets the last element in a list.",
|
||||||
Command: "rpushx",
|
HandleWithConnection: false,
|
||||||
Categories: []string{},
|
Sync: true,
|
||||||
Description: "(RPUSHX key value) Appends an element to the end of a list, only if the list exists.",
|
Plugin: ListModule,
|
||||||
HandleWithConnection: false,
|
},
|
||||||
Sync: true,
|
{
|
||||||
|
Command: "rpush",
|
||||||
|
Categories: []string{},
|
||||||
|
Description: "(RPUSH key value [value2]) Appends one or multiple elements to the end of a list.",
|
||||||
|
HandleWithConnection: false,
|
||||||
|
Sync: true,
|
||||||
|
Plugin: ListModule,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: "rpushx",
|
||||||
|
Categories: []string{},
|
||||||
|
Description: "(RPUSHX key value) Appends an element to the end of a list, only if the list exists.",
|
||||||
|
HandleWithConnection: false,
|
||||||
|
Sync: true,
|
||||||
|
Plugin: ListModule,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
description: "Handle List commands",
|
||||||
}
|
}
|
||||||
Plugin.description = "Handle List commands"
|
return ListModule
|
||||||
}
|
}
|
90
src/modules/ping/commands.go
Normal file
90
src/modules/ping/commands.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package ping
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"github.com/kelvinmwinuka/memstore/src/utils"
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
OK = "+OK\r\n\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Plugin struct {
|
||||||
|
name string
|
||||||
|
commands []utils.Command
|
||||||
|
description string
|
||||||
|
}
|
||||||
|
|
||||||
|
var PingModule Plugin
|
||||||
|
|
||||||
|
func (p Plugin) Name() string {
|
||||||
|
return p.name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) Commands() ([]byte, error) {
|
||||||
|
return json.Marshal(p.commands)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) GetCommands() []utils.Command {
|
||||||
|
return p.commands
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) Description() string {
|
||||||
|
return p.description
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) HandleCommandWithConnection(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
||||||
|
return nil, errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) HandleCommand(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
|
switch strings.ToLower(cmd[0]) {
|
||||||
|
default:
|
||||||
|
return nil, errors.New("not implemented")
|
||||||
|
case "ping":
|
||||||
|
return handlePing(ctx, cmd, server)
|
||||||
|
case "ack":
|
||||||
|
return []byte("$-1\r\n\n"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handlePing(ctx context.Context, cmd []string, s utils.Server) ([]byte, error) {
|
||||||
|
switch len(cmd) {
|
||||||
|
default:
|
||||||
|
return nil, errors.New("wrong number of arguments for PING command")
|
||||||
|
case 1:
|
||||||
|
return []byte("+PONG\r\n\n"), nil
|
||||||
|
case 2:
|
||||||
|
return []byte("+" + cmd[1] + "\r\n\n"), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewModule() Plugin {
|
||||||
|
PingModule := Plugin{
|
||||||
|
name: "PingCommands",
|
||||||
|
commands: []utils.Command{
|
||||||
|
{
|
||||||
|
Command: "ping",
|
||||||
|
Categories: []string{},
|
||||||
|
Description: "",
|
||||||
|
HandleWithConnection: false,
|
||||||
|
Sync: false,
|
||||||
|
Plugin: PingModule,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Command: "ack",
|
||||||
|
Categories: []string{},
|
||||||
|
Description: "",
|
||||||
|
HandleWithConnection: false,
|
||||||
|
Sync: false,
|
||||||
|
Plugin: PingModule,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
description: "Handle PING command",
|
||||||
|
}
|
||||||
|
return PingModule
|
||||||
|
}
|
@@ -1,77 +1,63 @@
|
|||||||
package main
|
package set
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/kelvinmwinuka/memstore/src/utils"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server interface {
|
|
||||||
KeyLock(ctx context.Context, key string) (bool, error)
|
|
||||||
KeyUnlock(key string)
|
|
||||||
KeyRLock(ctx context.Context, key string) (bool, error)
|
|
||||||
KeyRUnlock(key string)
|
|
||||||
KeyExists(key string) bool
|
|
||||||
CreateKeyAndLock(ctx context.Context, key string) (bool, error)
|
|
||||||
GetValue(key string) interface{}
|
|
||||||
SetValue(ctx context.Context, key string, value interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type KeyObject struct {
|
type KeyObject struct {
|
||||||
value interface{}
|
value interface{}
|
||||||
locked bool
|
locked bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type Command struct {
|
type Plugin struct {
|
||||||
Command string `json:"Command"`
|
|
||||||
Categories []string `json:"Categories"`
|
|
||||||
Description string `json:"Description"`
|
|
||||||
HandleWithConnection bool `json:"HandleWithConnection"`
|
|
||||||
Sync bool `json:"Sync"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type plugin struct {
|
|
||||||
name string
|
name string
|
||||||
commands []Command
|
commands []utils.Command
|
||||||
description string
|
description string
|
||||||
}
|
}
|
||||||
|
|
||||||
var Plugin plugin
|
var SetModule Plugin
|
||||||
|
|
||||||
func (p *plugin) Name() string {
|
func (p Plugin) Name() string {
|
||||||
return p.name
|
return p.name
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) Commands() ([]byte, error) {
|
func (p Plugin) Commands() ([]byte, error) {
|
||||||
return json.Marshal(p.commands)
|
return json.Marshal(p.commands)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) Description() string {
|
func (p Plugin) GetCommands() []utils.Command {
|
||||||
|
return p.commands
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) Description() string {
|
||||||
return p.description
|
return p.description
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) HandleCommandWithConnection(ctx context.Context, cmd []string, server interface{}, conn *net.Conn) ([]byte, error) {
|
func (p Plugin) HandleCommandWithConnection(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) HandleCommand(ctx context.Context, cmd []string, server interface{}) ([]byte, error) {
|
func (p Plugin) HandleCommand(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
switch strings.ToLower(cmd[0]) {
|
switch strings.ToLower(cmd[0]) {
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("command unknown")
|
return nil, errors.New("command unknown")
|
||||||
case "set":
|
case "set":
|
||||||
return handleSet(ctx, cmd, server.(Server))
|
return handleSet(ctx, cmd, server)
|
||||||
case "setnx":
|
case "setnx":
|
||||||
return handleSetNX(ctx, cmd, server.(Server))
|
return handleSetNX(ctx, cmd, server)
|
||||||
case "mset":
|
case "mset":
|
||||||
return handleMSet(ctx, cmd, server.(Server))
|
return handleMSet(ctx, cmd, server)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSet(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
func handleSet(ctx context.Context, cmd []string, s utils.Server) ([]byte, error) {
|
||||||
ctx, cancel := context.WithCancel(ctx)
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -84,7 +70,7 @@ func handleSet(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
|||||||
if !s.KeyExists(key) {
|
if !s.KeyExists(key) {
|
||||||
// TODO: Retry CreateKeyAndLock until we manage to obtain the key
|
// TODO: Retry CreateKeyAndLock until we manage to obtain the key
|
||||||
s.CreateKeyAndLock(ctx, key)
|
s.CreateKeyAndLock(ctx, key)
|
||||||
s.SetValue(ctx, key, AdaptType(cmd[2]))
|
s.SetValue(ctx, key, utils.AdaptType(cmd[2]))
|
||||||
s.KeyUnlock(key)
|
s.KeyUnlock(key)
|
||||||
return []byte("+OK\r\n\n"), nil
|
return []byte("+OK\r\n\n"), nil
|
||||||
}
|
}
|
||||||
@@ -93,13 +79,13 @@ func handleSet(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s.SetValue(ctx, key, AdaptType(cmd[2]))
|
s.SetValue(ctx, key, utils.AdaptType(cmd[2]))
|
||||||
s.KeyUnlock(key)
|
s.KeyUnlock(key)
|
||||||
return []byte("+OK\r\n\n"), nil
|
return []byte("+OK\r\n\n"), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSetNX(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
func handleSetNX(ctx context.Context, cmd []string, s utils.Server) ([]byte, error) {
|
||||||
switch x := len(cmd); {
|
switch x := len(cmd); {
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("wrong number of args for SETNX command")
|
return nil, errors.New("wrong number of args for SETNX command")
|
||||||
@@ -110,13 +96,13 @@ func handleSetNX(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
|||||||
}
|
}
|
||||||
// TODO: Retry CreateKeyAndLock until we manage to obtain the key
|
// TODO: Retry CreateKeyAndLock until we manage to obtain the key
|
||||||
s.CreateKeyAndLock(ctx, key)
|
s.CreateKeyAndLock(ctx, key)
|
||||||
s.SetValue(ctx, key, AdaptType(cmd[2]))
|
s.SetValue(ctx, key, utils.AdaptType(cmd[2]))
|
||||||
s.KeyUnlock(key)
|
s.KeyUnlock(key)
|
||||||
}
|
}
|
||||||
return []byte("+OK\r\n\n"), nil
|
return []byte("+OK\r\n\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMSet(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
func handleMSet(ctx context.Context, cmd []string, s utils.Server) ([]byte, error) {
|
||||||
ctx, cancel := context.WithTimeout(ctx, 250*time.Millisecond)
|
ctx, cancel := context.WithTimeout(ctx, 250*time.Millisecond)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
@@ -144,7 +130,7 @@ func handleMSet(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
|||||||
for i, key := range cmd[1:] {
|
for i, key := range cmd[1:] {
|
||||||
if i%2 == 0 {
|
if i%2 == 0 {
|
||||||
entries[key] = KeyObject{
|
entries[key] = KeyObject{
|
||||||
value: AdaptType(cmd[1:][i+1]),
|
value: utils.AdaptType(cmd[1:][i+1]),
|
||||||
locked: false,
|
locked: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,30 +160,37 @@ func handleMSet(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
|||||||
return []byte("+OK\r\n\n"), nil
|
return []byte("+OK\r\n\n"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func NewModule() Plugin {
|
||||||
Plugin.name = "SetCommands"
|
SetModule := Plugin{
|
||||||
Plugin.commands = []Command{
|
name: "SetCommands",
|
||||||
{
|
commands: []utils.Command{
|
||||||
Command: "set",
|
{
|
||||||
Categories: []string{},
|
Command: "set",
|
||||||
Description: "(SET key value) Set the value of a key, considering the value's type.",
|
Categories: []string{},
|
||||||
HandleWithConnection: false,
|
Description: "(SET key value) Set the value of a key, considering the value's type.",
|
||||||
Sync: true,
|
HandleWithConnection: false,
|
||||||
},
|
Sync: true,
|
||||||
{
|
Plugin: SetModule,
|
||||||
Command: "setnx",
|
},
|
||||||
Categories: []string{},
|
{
|
||||||
Description: "(SETNX key value) Set the key/value only if the key doesn't exist.",
|
Command: "setnx",
|
||||||
HandleWithConnection: false,
|
Categories: []string{},
|
||||||
Sync: true,
|
Description: "(SETNX key value) Set the key/value only if the key doesn't exist.",
|
||||||
},
|
HandleWithConnection: false,
|
||||||
{
|
Sync: true,
|
||||||
Command: "mset",
|
Plugin: SetModule,
|
||||||
Categories: []string{},
|
},
|
||||||
Description: "(MSET key value [key value ...]) Automatically set or modify multiple key/value pairs.",
|
{
|
||||||
HandleWithConnection: false,
|
Command: "mset",
|
||||||
Sync: true,
|
Categories: []string{},
|
||||||
|
Description: "(MSET key value [key value ...]) Automatically set or modify multiple key/value pairs.",
|
||||||
|
HandleWithConnection: false,
|
||||||
|
Sync: true,
|
||||||
|
Plugin: SetModule,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
description: "Handle basic SET commands",
|
||||||
}
|
}
|
||||||
Plugin.description = "Handle basic SET commands"
|
|
||||||
|
return SetModule
|
||||||
}
|
}
|
@@ -1,79 +1,66 @@
|
|||||||
package main
|
package str
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/kelvinmwinuka/memstore/src/utils"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server interface {
|
type Plugin struct {
|
||||||
KeyLock(ctx context.Context, key string) (bool, error)
|
|
||||||
KeyUnlock(key string)
|
|
||||||
KeyRLock(ctx context.Context, key string) (bool, error)
|
|
||||||
KeyRUnlock(key string)
|
|
||||||
KeyExists(key string) bool
|
|
||||||
CreateKeyAndLock(ctx context.Context, key string) (bool, error)
|
|
||||||
GetValue(key string) interface{}
|
|
||||||
SetValue(ctx context.Context, key string, value interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Command struct {
|
|
||||||
Command string `json:"Command"`
|
|
||||||
Categories []string `json:"Categories"`
|
|
||||||
Description string `json:"Description"`
|
|
||||||
HandleWithConnection bool `json:"HandleWithConnection"`
|
|
||||||
Sync bool `json:"Sync"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type plugin struct {
|
|
||||||
name string
|
name string
|
||||||
commands []Command
|
commands []utils.Command
|
||||||
description string
|
description string
|
||||||
}
|
}
|
||||||
|
|
||||||
var Plugin plugin
|
var StringModule Plugin
|
||||||
|
|
||||||
func (p *plugin) Name() string {
|
func (p Plugin) Name() string {
|
||||||
return p.name
|
return p.name
|
||||||
}
|
}
|
||||||
func (p *plugin) Commands() ([]byte, error) {
|
|
||||||
|
func (p Plugin) Commands() ([]byte, error) {
|
||||||
return json.Marshal(p.commands)
|
return json.Marshal(p.commands)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) Description() string {
|
func (p Plugin) GetCommands() []utils.Command {
|
||||||
|
return p.commands
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p Plugin) Description() string {
|
||||||
return p.description
|
return p.description
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) HandleCommandWithConnection(ctx context.Context, cmd []string, server interface{}, conn *net.Conn) ([]byte, error) {
|
func (p Plugin) HandleCommandWithConnection(ctx context.Context, cmd []string, server utils.Server, conn *net.Conn) ([]byte, error) {
|
||||||
return nil, errors.New("not implemented")
|
return nil, errors.New("not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *plugin) HandleCommand(ctx context.Context, cmd []string, server interface{}) ([]byte, error) {
|
func (p Plugin) HandleCommand(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
switch strings.ToLower(cmd[0]) {
|
switch strings.ToLower(cmd[0]) {
|
||||||
default:
|
default:
|
||||||
return nil, errors.New("command unknown")
|
return nil, errors.New("command unknown")
|
||||||
case "setrange":
|
case "setrange":
|
||||||
return handleSetRange(ctx, cmd, server.(Server))
|
return handleSetRange(ctx, cmd, server)
|
||||||
case "strlen":
|
case "strlen":
|
||||||
return handleStrLen(ctx, cmd, server.(Server))
|
return handleStrLen(ctx, cmd, server)
|
||||||
case "substr":
|
case "substr":
|
||||||
return handleSubStr(ctx, cmd, server.(Server))
|
return handleSubStr(ctx, cmd, server)
|
||||||
case "getrange":
|
case "getrange":
|
||||||
return handleSubStr(ctx, cmd, server.(Server))
|
return handleSubStr(ctx, cmd, server)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSetRange(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleSetRange(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd[1:]) != 3 {
|
if len(cmd[1:]) != 3 {
|
||||||
return nil, errors.New("wrong number of args for SETRANGE command")
|
return nil, errors.New("wrong number of args for SETRANGE command")
|
||||||
}
|
}
|
||||||
|
|
||||||
key := cmd[1]
|
key := cmd[1]
|
||||||
|
|
||||||
offset, ok := AdaptType(cmd[2]).(int64)
|
offset, ok := utils.AdaptType(cmd[2]).(int64)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("offset must be integer")
|
return nil, errors.New("offset must be integer")
|
||||||
}
|
}
|
||||||
@@ -132,7 +119,7 @@ func handleSetRange(ctx context.Context, cmd []string, server Server) ([]byte, e
|
|||||||
return []byte(fmt.Sprintf(":%d\r\n\n", len(newStr))), nil
|
return []byte(fmt.Sprintf(":%d\r\n\n", len(newStr))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStrLen(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleStrLen(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd[1:]) != 1 {
|
if len(cmd[1:]) != 1 {
|
||||||
return nil, errors.New("wrong number of args for STRLEN command")
|
return nil, errors.New("wrong number of args for STRLEN command")
|
||||||
}
|
}
|
||||||
@@ -157,15 +144,15 @@ func handleStrLen(ctx context.Context, cmd []string, server Server) ([]byte, err
|
|||||||
return []byte(fmt.Sprintf(":%d\r\n\n", len(value))), nil
|
return []byte(fmt.Sprintf(":%d\r\n\n", len(value))), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSubStr(ctx context.Context, cmd []string, server Server) ([]byte, error) {
|
func handleSubStr(ctx context.Context, cmd []string, server utils.Server) ([]byte, error) {
|
||||||
if len(cmd[1:]) != 3 {
|
if len(cmd[1:]) != 3 {
|
||||||
return nil, errors.New("wrong number of args for SUBSTR command")
|
return nil, errors.New("wrong number of args for SUBSTR command")
|
||||||
}
|
}
|
||||||
|
|
||||||
key := cmd[1]
|
key := cmd[1]
|
||||||
|
|
||||||
start, startOk := AdaptType(cmd[2]).(int64)
|
start, startOk := utils.AdaptType(cmd[2]).(int64)
|
||||||
end, endOk := AdaptType(cmd[3]).(int64)
|
end, endOk := utils.AdaptType(cmd[3]).(int64)
|
||||||
reversed := false
|
reversed := false
|
||||||
|
|
||||||
if !startOk || !endOk {
|
if !startOk || !endOk {
|
||||||
@@ -220,37 +207,44 @@ func handleSubStr(ctx context.Context, cmd []string, server Server) ([]byte, err
|
|||||||
return []byte(fmt.Sprintf("$%d\r\n%s\r\n\n", len(str), str)), nil
|
return []byte(fmt.Sprintf("$%d\r\n%s\r\n\n", len(str), str)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func NewModule() Plugin {
|
||||||
Plugin.name = "StringCommands"
|
StringModule := Plugin{
|
||||||
Plugin.commands = []Command{
|
name: "StringCommands",
|
||||||
{
|
commands: []utils.Command{
|
||||||
Command: "setrange",
|
{
|
||||||
Categories: []string{},
|
Command: "setrange",
|
||||||
Description: "(SETRANGE key offset value) Overwrites part of a string value with another by offset. Creates the key if it doesn't exist.",
|
Categories: []string{},
|
||||||
HandleWithConnection: false,
|
Description: "(SETRANGE key offset value) Overwrites part of a string value with another by offset. Creates the key if it doesn't exist.",
|
||||||
Sync: true,
|
HandleWithConnection: false,
|
||||||
},
|
Sync: true,
|
||||||
{
|
Plugin: StringModule,
|
||||||
Command: "strlen",
|
},
|
||||||
Categories: []string{},
|
{
|
||||||
Description: "(STRLEN key) Returns length of the key's value if it's a string.",
|
Command: "strlen",
|
||||||
HandleWithConnection: false,
|
Categories: []string{},
|
||||||
Sync: false,
|
Description: "(STRLEN key) Returns length of the key's value if it's a string.",
|
||||||
},
|
HandleWithConnection: false,
|
||||||
{
|
Sync: false,
|
||||||
Command: "substr",
|
Plugin: StringModule,
|
||||||
Categories: []string{},
|
},
|
||||||
Description: "(SUBSTR key start end) Returns a substring from the string value.",
|
{
|
||||||
HandleWithConnection: false,
|
Command: "substr",
|
||||||
Sync: false,
|
Categories: []string{},
|
||||||
},
|
Description: "(SUBSTR key start end) Returns a substring from the string value.",
|
||||||
{
|
HandleWithConnection: false,
|
||||||
Command: "getrange",
|
Sync: false,
|
||||||
Categories: []string{},
|
Plugin: StringModule,
|
||||||
Description: "(GETRANGE key start end) Returns a substring from the string value.",
|
},
|
||||||
HandleWithConnection: false,
|
{
|
||||||
Sync: false,
|
Command: "getrange",
|
||||||
|
Categories: []string{},
|
||||||
|
Description: "(GETRANGE key start end) Returns a substring from the string value.",
|
||||||
|
HandleWithConnection: false,
|
||||||
|
Sync: false,
|
||||||
|
Plugin: StringModule,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
description: "Handle basic STRING commands",
|
||||||
}
|
}
|
||||||
Plugin.description = "Handle basic STRING commands"
|
return StringModule
|
||||||
}
|
}
|
@@ -1,137 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Server interface {
|
|
||||||
KeyLock(ctx context.Context, key string) (bool, error)
|
|
||||||
KeyUnlock(key string)
|
|
||||||
KeyRLock(ctx context.Context, key string) (bool, error)
|
|
||||||
KeyRUnlock(key string)
|
|
||||||
KeyExists(key string) bool
|
|
||||||
CreateKeyAndLock(ctx context.Context, key string) (bool, error)
|
|
||||||
GetValue(key string) interface{}
|
|
||||||
SetValue(ctx context.Context, key string, value interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Command struct {
|
|
||||||
Command string `json:"Command"`
|
|
||||||
Categories []string `json:"Categories"`
|
|
||||||
Description string `json:"Description"`
|
|
||||||
HandleWithConnection bool `json:"HandleWithConnection"`
|
|
||||||
Sync bool `json:"Sync"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type plugin struct {
|
|
||||||
name string
|
|
||||||
commands []Command
|
|
||||||
categories []string
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
var Plugin plugin
|
|
||||||
|
|
||||||
func (p *plugin) Name() string {
|
|
||||||
return p.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *plugin) Commands() ([]byte, error) {
|
|
||||||
return json.Marshal(p.commands)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *plugin) Description() string {
|
|
||||||
return p.description
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *plugin) HandleCommandWithConnection(ctx context.Context, cmd []string, server interface{}, conn *net.Conn) ([]byte, error) {
|
|
||||||
return nil, errors.New("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *plugin) HandleCommand(ctx context.Context, cmd []string, server interface{}) ([]byte, error) {
|
|
||||||
switch strings.ToLower(cmd[0]) {
|
|
||||||
default:
|
|
||||||
return nil, errors.New("command unknown")
|
|
||||||
case "get":
|
|
||||||
return handleGet(ctx, cmd, server.(Server))
|
|
||||||
case "mget":
|
|
||||||
return handleMGet(ctx, cmd, server.(Server))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleGet(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
|
||||||
if len(cmd) != 2 {
|
|
||||||
return nil, errors.New("wrong number of args for GET command")
|
|
||||||
}
|
|
||||||
|
|
||||||
key := cmd[1]
|
|
||||||
|
|
||||||
s.KeyRLock(ctx, key)
|
|
||||||
value := s.GetValue(key)
|
|
||||||
s.KeyRUnlock(key)
|
|
||||||
|
|
||||||
switch value.(type) {
|
|
||||||
default:
|
|
||||||
return []byte(fmt.Sprintf("+%v\r\n\n", value)), nil
|
|
||||||
case nil:
|
|
||||||
return []byte("+nil\r\n\n"), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleMGet(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
|
||||||
if len(cmd) < 2 {
|
|
||||||
return nil, errors.New("wrong number of args for MGET command")
|
|
||||||
}
|
|
||||||
|
|
||||||
vals := []string{}
|
|
||||||
|
|
||||||
for _, key := range cmd[1:] {
|
|
||||||
func(key string) {
|
|
||||||
s.KeyRLock(ctx, key)
|
|
||||||
switch s.GetValue(key).(type) {
|
|
||||||
default:
|
|
||||||
vals = append(vals, fmt.Sprintf("%v", s.GetValue(key)))
|
|
||||||
case nil:
|
|
||||||
vals = append(vals, "nil")
|
|
||||||
}
|
|
||||||
s.KeyRUnlock(key)
|
|
||||||
|
|
||||||
}(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
var bytes []byte = []byte(fmt.Sprintf("*%d\r\n", len(vals)))
|
|
||||||
|
|
||||||
for _, val := range vals {
|
|
||||||
bytes = append(bytes, []byte(fmt.Sprintf("$%d\r\n%s\r\n", len(val), val))...)
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes = append(bytes, []byte("\n")...)
|
|
||||||
|
|
||||||
return bytes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
Plugin.name = "GetCommands"
|
|
||||||
Plugin.commands = []Command{
|
|
||||||
{
|
|
||||||
Command: "get",
|
|
||||||
Categories: []string{},
|
|
||||||
Description: "",
|
|
||||||
HandleWithConnection: false,
|
|
||||||
Sync: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Command: "mget",
|
|
||||||
Categories: []string{},
|
|
||||||
Description: "",
|
|
||||||
HandleWithConnection: false,
|
|
||||||
Sync: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Plugin.description = "Handle basic GET and MGET commands"
|
|
||||||
}
|
|
@@ -1,39 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Contains[T comparable](arr []T, elem T) bool {
|
|
||||||
for _, v := range arr {
|
|
||||||
if v == elem {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func Filter[T comparable](arr []T, test func(elem T) bool) (res []T) {
|
|
||||||
for _, e := range arr {
|
|
||||||
if test(e) {
|
|
||||||
res = append(res, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func AdaptType(s string) interface{} {
|
|
||||||
// Adapt the type of the parameter to string, float64 or int
|
|
||||||
n, _, err := big.ParseFloat(s, 10, 256, big.RoundingMode(big.Exact))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.IsInt() {
|
|
||||||
i, _ := n.Int64()
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
return n
|
|
||||||
}
|
|
@@ -1,99 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
OK = "+OK\r\n\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Server interface {
|
|
||||||
KeyLock(ctx context.Context, key string) (bool, error)
|
|
||||||
KeyUnlock(key string)
|
|
||||||
KeyRLock(ctx context.Context, key string) (bool, error)
|
|
||||||
KeyRUnlock(key string)
|
|
||||||
KeyExists(key string) bool
|
|
||||||
CreateKeyAndLock(ctx context.Context, key string) (bool, error)
|
|
||||||
GetValue(key string) interface{}
|
|
||||||
SetValue(ctx context.Context, key string, value interface{})
|
|
||||||
}
|
|
||||||
|
|
||||||
type Command struct {
|
|
||||||
Command string `json:"Command"`
|
|
||||||
Categories []string `json:"Categories"`
|
|
||||||
Description string `json:"Description"`
|
|
||||||
HandleWithConnection bool `json:"HandleWithConnection"`
|
|
||||||
Sync bool `json:"Sync"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type plugin struct {
|
|
||||||
name string
|
|
||||||
commands []Command
|
|
||||||
description string
|
|
||||||
}
|
|
||||||
|
|
||||||
var Plugin plugin
|
|
||||||
|
|
||||||
func (p *plugin) Name() string {
|
|
||||||
return p.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *plugin) Commands() ([]byte, error) {
|
|
||||||
return json.Marshal(p.commands)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *plugin) Description() string {
|
|
||||||
return p.description
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *plugin) HandleCommandWithConnection(ctx context.Context, cmd []string, server interface{}, conn *net.Conn) ([]byte, error) {
|
|
||||||
return nil, errors.New("not implemented")
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *plugin) HandleCommand(ctx context.Context, cmd []string, server interface{}) ([]byte, error) {
|
|
||||||
switch strings.ToLower(cmd[0]) {
|
|
||||||
default:
|
|
||||||
return nil, errors.New("not implemented")
|
|
||||||
case "ping":
|
|
||||||
return handlePing(ctx, cmd, server.(Server))
|
|
||||||
case "ack":
|
|
||||||
return []byte("$-1\r\n\n"), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handlePing(ctx context.Context, cmd []string, s Server) ([]byte, error) {
|
|
||||||
switch len(cmd) {
|
|
||||||
default:
|
|
||||||
return nil, errors.New("wrong number of arguments for PING command")
|
|
||||||
case 1:
|
|
||||||
return []byte("+PONG\r\n\n"), nil
|
|
||||||
case 2:
|
|
||||||
return []byte("+" + cmd[1] + "\r\n\n"), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
Plugin.name = "PingCommands"
|
|
||||||
Plugin.commands = []Command{
|
|
||||||
{
|
|
||||||
Command: "ping",
|
|
||||||
Categories: []string{},
|
|
||||||
Description: "",
|
|
||||||
HandleWithConnection: false,
|
|
||||||
Sync: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Command: "ack",
|
|
||||||
Categories: []string{},
|
|
||||||
Description: "",
|
|
||||||
HandleWithConnection: false,
|
|
||||||
Sync: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
Plugin.description = "Handle PING command"
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
)
|
|
||||||
|
|
||||||
func AdaptType(s string) interface{} {
|
|
||||||
// Adapt the type of the parameter to string, float64 or int
|
|
||||||
n, _, err := big.ParseFloat(s, 10, 256, big.RoundingMode(big.Exact))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.IsInt() {
|
|
||||||
i, _ := n.Int64()
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
return n
|
|
||||||
}
|
|
@@ -1,21 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/big"
|
|
||||||
)
|
|
||||||
|
|
||||||
func AdaptType(s string) interface{} {
|
|
||||||
// Adapt the type of the parameter to string, float64 or int
|
|
||||||
n, _, err := big.ParseFloat(s, 10, 256, big.RoundingMode(big.Exact))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
if n.IsInt() {
|
|
||||||
i, _ := n.Int64()
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
return n
|
|
||||||
}
|
|
@@ -5,6 +5,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
@@ -13,6 +14,22 @@ import (
|
|||||||
"github.com/tidwall/resp"
|
"github.com/tidwall/resp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func AdaptType(s string) interface{} {
|
||||||
|
// Adapt the type of the parameter to string, float64 or int
|
||||||
|
n, _, err := big.ParseFloat(s, 10, 256, big.RoundingMode(big.Exact))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.IsInt() {
|
||||||
|
i, _ := n.Int64()
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
func Contains[T comparable](arr []T, elem T) bool {
|
func Contains[T comparable](arr []T, elem T) bool {
|
||||||
for _, v := range arr {
|
for _, v := range arr {
|
||||||
if v == elem {
|
if v == elem {
|
||||||
|
Reference in New Issue
Block a user