Uncommented AddCommand method

This commit is contained in:
Kelvin Clement Mwinuka
2024-05-26 15:26:04 +08:00
parent e3ecc42454
commit abee9ea858
5 changed files with 1459 additions and 231 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -16,6 +16,7 @@ package echovault
import (
"context"
"fmt"
"github.com/echovault/echovault/internal"
"slices"
"strings"
@@ -84,14 +85,9 @@ type CommandHandlerFunc func(params CommandHandlerFuncParams) ([]byte, error)
type CommandHandlerFuncParams struct {
Context context.Context
Command []string
KeyExists func(ctx context.Context, key string) bool
CreateKeyAndLock func(ctx context.Context, key string) (bool, error)
KeyLock func(ctx context.Context, key string) (bool, error)
KeyUnlock func(ctx context.Context, key string)
KeyRLock func(ctx context.Context, key string) (bool, error)
KeyRUnlock func(ctx context.Context, key string)
GetValue func(ctx context.Context, key string) interface{}
SetValue func(ctx context.Context, key string, value interface{}) error
KeysExist func(keys []string) map[string]bool
GetValues func(ctx context.Context, keys []string) map[string]interface{}
SetValues func(ctx context.Context, entries map[string]interface{}) error
}
// CommandOptions provides the specification of the command to be added to the EchoVault instance.
@@ -229,133 +225,123 @@ func (server *EchoVault) RewriteAOF() (string, error) {
// Errors:
//
// "command <command> already exists" - If a command with the same command name as the passed command already exists.
// func (server *EchoVault) AddCommand(command CommandOptions) error {
// server.commandsRWMut.Lock()
// defer server.commandsRWMut.Unlock()
// // Check if command already exists
// for _, c := range server.commands {
// if strings.EqualFold(c.Command, command.Command) {
// return fmt.Errorf("command %s already exists", command.Command)
// }
// }
//
// if command.SubCommand == nil || len(command.SubCommand) == 0 {
// // Add command with no subcommands
// server.commands = append(server.commands, internal.Command{
// Command: command.Command,
// Module: strings.ToLower(command.Module), // Convert module to lower case for uniformity
// Categories: func() []string {
// // Convert all the categories to lower case for uniformity
// cats := make([]string, len(command.Categories))
// for i, cat := range command.Categories {
// cats[i] = strings.ToLower(cat)
// }
// return cats
// }(),
// Description: command.Description,
// Sync: command.Sync,
// KeyExtractionFunc: internal.KeyExtractionFunc(func(cmd []string) (internal.KeyExtractionFuncResult, error) {
// accessKeys, err := command.KeyExtractionFunc(cmd)
// if err != nil {
// return internal.KeyExtractionFuncResult{}, err
// }
// return internal.KeyExtractionFuncResult{
// Channels: []string{},
// ReadKeys: accessKeys.ReadKeys,
// WriteKeys: accessKeys.WriteKeys,
// }, nil
// }),
// HandlerFunc: internal.HandlerFunc(func(params internal.HandlerFuncParams) ([]byte, error) {
// return command.HandlerFunc(CommandHandlerFuncParams{
// Context: params.Context,
// Command: params.Command,
// KeyLock: params.KeyLock,
// KeyUnlock: params.KeyUnlock,
// KeyRLock: params.KeyRLock,
// KeyRUnlock: params.KeyRUnlock,
// KeyExists: params.KeyExists,
// CreateKeyAndLock: params.CreateKeyAndLock,
// GetValue: params.GetValue,
// SetValue: params.SetValue,
// })
// }),
// })
// return nil
// }
//
// // Add command with subcommands
// newCommand := internal.Command{
// Command: command.Command,
// Module: command.Module,
// Categories: func() []string {
// // Convert all the categories to lower case for uniformity
// cats := make([]string, len(command.Categories))
// for j, cat := range command.Categories {
// cats[j] = strings.ToLower(cat)
// }
// return cats
// }(),
// Description: command.Description,
// Sync: command.Sync,
// KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
// return internal.KeyExtractionFuncResult{}, nil
// },
// HandlerFunc: func(param internal.HandlerFuncParams) ([]byte, error) { return nil, nil },
// SubCommands: make([]internal.SubCommand, len(command.SubCommand)),
// }
//
// for i, sc := range command.SubCommand {
// // Skip the subcommand if it already exists in newCommand
// if slices.ContainsFunc(newCommand.SubCommands, func(subcommand internal.SubCommand) bool {
// return strings.EqualFold(subcommand.Command, sc.Command)
// }) {
// continue
// }
// newCommand.SubCommands[i] = internal.SubCommand{
// Command: sc.Command,
// Module: strings.ToLower(command.Module),
// Categories: func() []string {
// // Convert all the categories to lower case for uniformity
// cats := make([]string, len(sc.Categories))
// for j, cat := range sc.Categories {
// cats[j] = strings.ToLower(cat)
// }
// return cats
// }(),
// Description: sc.Description,
// Sync: sc.Sync,
// KeyExtractionFunc: internal.KeyExtractionFunc(func(cmd []string) (internal.KeyExtractionFuncResult, error) {
// accessKeys, err := sc.KeyExtractionFunc(cmd)
// if err != nil {
// return internal.KeyExtractionFuncResult{}, err
// }
// return internal.KeyExtractionFuncResult{
// Channels: []string{},
// ReadKeys: accessKeys.ReadKeys,
// WriteKeys: accessKeys.WriteKeys,
// }, nil
// }),
// HandlerFunc: internal.HandlerFunc(func(params internal.HandlerFuncParams) ([]byte, error) {
// return sc.HandlerFunc(CommandHandlerFuncParams{
// Context: params.Context,
// Command: params.Command,
// KeyLock: params.KeyLock,
// KeyUnlock: params.KeyUnlock,
// KeyRLock: params.KeyRLock,
// KeyRUnlock: params.KeyRUnlock,
// KeyExists: params.KeyExists,
// CreateKeyAndLock: params.CreateKeyAndLock,
// GetValue: params.GetValue,
// SetValue: params.SetValue,
// })
// }),
// }
// }
//
// server.commands = append(server.commands, newCommand)
//
// return nil
// }
func (server *EchoVault) AddCommand(command CommandOptions) error {
server.commandsRWMut.Lock()
defer server.commandsRWMut.Unlock()
// Check if command already exists
for _, c := range server.commands {
if strings.EqualFold(c.Command, command.Command) {
return fmt.Errorf("command %s already exists", command.Command)
}
}
if command.SubCommand == nil || len(command.SubCommand) == 0 {
// Add command with no subcommands
server.commands = append(server.commands, internal.Command{
Command: command.Command,
Module: strings.ToLower(command.Module), // Convert module to lower case for uniformity
Categories: func() []string {
// Convert all the categories to lower case for uniformity
cats := make([]string, len(command.Categories))
for i, cat := range command.Categories {
cats[i] = strings.ToLower(cat)
}
return cats
}(),
Description: command.Description,
Sync: command.Sync,
KeyExtractionFunc: internal.KeyExtractionFunc(func(cmd []string) (internal.KeyExtractionFuncResult, error) {
accessKeys, err := command.KeyExtractionFunc(cmd)
if err != nil {
return internal.KeyExtractionFuncResult{}, err
}
return internal.KeyExtractionFuncResult{
Channels: []string{},
ReadKeys: accessKeys.ReadKeys,
WriteKeys: accessKeys.WriteKeys,
}, nil
}),
HandlerFunc: internal.HandlerFunc(func(params internal.HandlerFuncParams) ([]byte, error) {
return command.HandlerFunc(CommandHandlerFuncParams{
Context: params.Context,
Command: params.Command,
KeysExist: params.KeysExist,
GetValues: params.GetValues,
SetValues: params.SetValues,
})
}),
})
return nil
}
// Add command with subcommands
newCommand := internal.Command{
Command: command.Command,
Module: command.Module,
Categories: func() []string {
// Convert all the categories to lower case for uniformity
cats := make([]string, len(command.Categories))
for j, cat := range command.Categories {
cats[j] = strings.ToLower(cat)
}
return cats
}(),
Description: command.Description,
Sync: command.Sync,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{}, nil
},
HandlerFunc: func(param internal.HandlerFuncParams) ([]byte, error) { return nil, nil },
SubCommands: make([]internal.SubCommand, len(command.SubCommand)),
}
for i, sc := range command.SubCommand {
// Skip the subcommand if it already exists in newCommand
if slices.ContainsFunc(newCommand.SubCommands, func(subcommand internal.SubCommand) bool {
return strings.EqualFold(subcommand.Command, sc.Command)
}) {
continue
}
newCommand.SubCommands[i] = internal.SubCommand{
Command: sc.Command,
Module: strings.ToLower(command.Module),
Categories: func() []string {
// Convert all the categories to lower case for uniformity
cats := make([]string, len(sc.Categories))
for j, cat := range sc.Categories {
cats[j] = strings.ToLower(cat)
}
return cats
}(),
Description: sc.Description,
Sync: sc.Sync,
KeyExtractionFunc: internal.KeyExtractionFunc(func(cmd []string) (internal.KeyExtractionFuncResult, error) {
accessKeys, err := sc.KeyExtractionFunc(cmd)
if err != nil {
return internal.KeyExtractionFuncResult{}, err
}
return internal.KeyExtractionFuncResult{
Channels: []string{},
ReadKeys: accessKeys.ReadKeys,
WriteKeys: accessKeys.WriteKeys,
}, nil
}),
HandlerFunc: internal.HandlerFunc(func(params internal.HandlerFuncParams) ([]byte, error) {
return sc.HandlerFunc(CommandHandlerFuncParams{
Context: params.Context,
Command: params.Command,
KeysExist: params.KeysExist,
GetValues: params.GetValues,
SetValues: params.SetValues,
})
}),
}
}
server.commands = append(server.commands, newCommand)
return nil
}
// ExecuteCommand executes the command passed to it. If 1 string is passed, EchoVault will try to
// execute the command. If 2 strings are passed, EchoVault will attempt to execute the subcommand of the command.

View File

@@ -159,14 +159,13 @@ func NewEchoVault(options ...func(echovault *EchoVault)) (*EchoVault, error) {
)
// Load .so modules from config
// TODO: Uncomment this
// for _, path := range echovault.config.Modules {
// if err := echovault.LoadModule(path); err != nil {
// log.Printf("%s %v\n", path, err)
// continue
// }
// log.Printf("loaded plugin %s\n", path)
// }
for _, path := range echovault.config.Modules {
if err := echovault.LoadModule(path); err != nil {
log.Printf("%s %v\n", path, err)
continue
}
log.Printf("loaded plugin %s\n", path)
}
// Function for server commands retrieval
echovault.getCommands = func() []internal.Command {

View File

@@ -40,14 +40,9 @@ func KeyExtractionFunc(cmd []string, args ...string) ([]string, []string, error)
func HandlerFunc(
ctx context.Context,
command []string,
keyExists func(ctx context.Context, key string) bool,
keyLock func(ctx context.Context, key string) (bool, error),
keyUnlock func(ctx context.Context, key string),
keyRLock func(ctx context.Context, key string) (bool, error),
keyRUnlock func(ctx context.Context, key string),
createKeyAndLock func(ctx context.Context, key string) (bool, error),
getValue func(ctx context.Context, key string) interface{},
setValue func(ctx context.Context, key string, value interface{}) error,
keysExist func(keys []string) map[string]bool,
getValues func(ctx context.Context, keys []string) map[string]interface{},
setValues func(ctx context.Context, entries map[string]interface{}) error,
args ...string) ([]byte, error) {
readKeys, _, err := KeyExtractionFunc(command, args...)
@@ -55,18 +50,13 @@ func HandlerFunc(
return nil, err
}
key := readKeys[0]
exists := keysExist(readKeys)[key]
if !keyExists(ctx, key) {
if !exists {
return []byte(":0\r\n"), nil
}
_, err = keyRLock(ctx, key)
if err != nil {
return nil, err
}
defer keyRUnlock(ctx, key)
val, ok := getValue(ctx, key).(int64)
val, ok := getValues(ctx, []string{key})[key].(int64)
if !ok {
return nil, fmt.Errorf("value at key %s is not an integer", key)
}

View File

@@ -40,14 +40,9 @@ func KeyExtractionFunc(cmd []string, args ...string) ([]string, []string, error)
func HandlerFunc(
ctx context.Context,
command []string,
keyExists func(ctx context.Context, key string) bool,
keyLock func(ctx context.Context, key string) (bool, error),
keyUnlock func(ctx context.Context, key string),
keyRLock func(ctx context.Context, key string) (bool, error),
keyRUnlock func(ctx context.Context, key string),
createKeyAndLock func(ctx context.Context, key string) (bool, error),
getValue func(ctx context.Context, key string) interface{},
setValue func(ctx context.Context, key string, value interface{}) error,
keysExist func(keys []string) map[string]bool,
getValues func(ctx context.Context, keys []string) map[string]interface{},
setValues func(ctx context.Context, entries map[string]interface{}) error,
args ...string) ([]byte, error) {
_, writeKeys, err := KeyExtractionFunc(command, args...)
@@ -56,25 +51,12 @@ func HandlerFunc(
}
key := writeKeys[0]
if !keyExists(ctx, key) {
_, err := createKeyAndLock(ctx, key)
if err != nil {
return nil, err
}
} else {
_, err := keyLock(ctx, key)
if err != nil {
return nil, err
}
}
defer keyUnlock(ctx, key)
value, err := strconv.ParseInt(command[2], 10, 64)
if err != nil {
return nil, err
}
err = setValue(ctx, key, value)
err = setValues(ctx, map[string]interface{}{key: value})
if err != nil {
return nil, err
}