mirror of
https://github.com/EchoVault/SugarDB.git
synced 2025-10-04 15:42:40 +08:00
284 lines
9.4 KiB
Go
284 lines
9.4 KiB
Go
// Copyright 2024 Kelvin Clement Mwinuka
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");s
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package echovault
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/echovault/echovault/internal"
|
|
"github.com/echovault/echovault/types"
|
|
"slices"
|
|
"strings"
|
|
)
|
|
|
|
// CommandListOptions modifies the result from the CommandList command.
|
|
//
|
|
// ACLCAT filters the results by the provided category. Has the highest priority.
|
|
//
|
|
// PATTERN filters the result that match the given glob pattern. Has the second-highest priority.
|
|
//
|
|
// MODULE filters the result by the provided module. Has the lowest priority.
|
|
type CommandListOptions struct {
|
|
ACLCAT string
|
|
PATTERN string
|
|
MODULE string
|
|
}
|
|
|
|
// TODO: Write godoc comment for CommandOptions type
|
|
type CommandOptions struct {
|
|
Command string
|
|
Module string
|
|
Categories []string
|
|
Description string
|
|
SubCommand []SubCommandOptions
|
|
Sync bool
|
|
KeyExtractionFunc types.PluginKeyExtractionFunc
|
|
HandlerFunc types.PluginHandlerFunc
|
|
}
|
|
|
|
// TODO: Write godoc comment for SubCommandOptions type
|
|
type SubCommandOptions struct {
|
|
Command string
|
|
Module string
|
|
Categories []string
|
|
Description string
|
|
Sync bool
|
|
KeyExtractionFunc types.PluginKeyExtractionFunc
|
|
HandlerFunc types.PluginHandlerFunc
|
|
}
|
|
|
|
// CommandList returns the list of commands currently loaded in the EchoVault instance.
|
|
//
|
|
// Parameters:
|
|
//
|
|
// `options` - CommandListOptions.
|
|
//
|
|
// Returns: a string slice of all the loaded commands. SubCommands are represented as "command|subcommand".
|
|
func (server *EchoVault) CommandList(options CommandListOptions) ([]string, error) {
|
|
cmd := []string{"COMMAND", "LIST"}
|
|
|
|
switch {
|
|
case options.ACLCAT != "":
|
|
cmd = append(cmd, []string{"FILTERBY", "ACLCAT", options.ACLCAT}...)
|
|
case options.PATTERN != "":
|
|
cmd = append(cmd, []string{"FILTERBY", "PATTERN", options.PATTERN}...)
|
|
case options.MODULE != "":
|
|
cmd = append(cmd, []string{"FILTERBY", "MODULE", options.MODULE}...)
|
|
}
|
|
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return internal.ParseStringArrayResponse(b)
|
|
}
|
|
|
|
// CommandCount returns the number of commands currently loaded in the EchoVault instance.
|
|
//
|
|
// Returns: integer representing the count of all available commands.
|
|
func (server *EchoVault) CommandCount() (int, error) {
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"COMMAND", "COUNT"}), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// Save triggers a new snapshot.
|
|
func (server *EchoVault) Save() (string, error) {
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"SAVE"}), nil, false, true)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return internal.ParseStringResponse(b)
|
|
}
|
|
|
|
// LastSave returns the unix epoch milliseconds timestamp of the last save.
|
|
func (server *EchoVault) LastSave() (int, error) {
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"LASTSAVE"}), nil, false, true)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return internal.ParseIntegerResponse(b)
|
|
}
|
|
|
|
// RewriteAOF triggers a compaction of the AOF file.
|
|
func (server *EchoVault) RewriteAOF() (string, error) {
|
|
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"REWRITEAOF"}), nil, false, true)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return internal.ParseStringResponse(b)
|
|
}
|
|
|
|
// TODO: Write godoc comment for AddCommand method
|
|
func (server *EchoVault) AddCommand(command CommandOptions) error {
|
|
// Check if commands 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(types.PluginHandlerFuncParams{
|
|
Context: params.Context,
|
|
Command: params.Command,
|
|
Connection: params.Connection,
|
|
KeyLock: params.KeyLock,
|
|
KeyUnlock: params.KeyUnlock,
|
|
KeyRLock: params.KeyRLock,
|
|
KeyRUnlock: params.KeyRUnlock,
|
|
KeyExists: params.KeyExists,
|
|
CreateKeyAndLock: params.CreateKeyAndLock,
|
|
GetValue: params.GetValue,
|
|
SetValue: params.SetValue,
|
|
GetExpiry: params.GetExpiry,
|
|
SetExpiry: params.SetExpiry,
|
|
RemoveExpiry: params.RemoveExpiry,
|
|
DeleteKey: params.DeleteKey,
|
|
})
|
|
}),
|
|
})
|
|
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 {
|
|
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(types.PluginHandlerFuncParams{
|
|
Context: params.Context,
|
|
Command: params.Command,
|
|
Connection: params.Connection,
|
|
KeyLock: params.KeyLock,
|
|
KeyUnlock: params.KeyUnlock,
|
|
KeyRLock: params.KeyRLock,
|
|
KeyRUnlock: params.KeyRUnlock,
|
|
KeyExists: params.KeyExists,
|
|
CreateKeyAndLock: params.CreateKeyAndLock,
|
|
GetValue: params.GetValue,
|
|
SetValue: params.SetValue,
|
|
GetExpiry: params.GetExpiry,
|
|
SetExpiry: params.SetExpiry,
|
|
RemoveExpiry: params.RemoveExpiry,
|
|
DeleteKey: params.DeleteKey,
|
|
})
|
|
}),
|
|
}
|
|
}
|
|
|
|
server.commands = append(server.commands, newCommand)
|
|
|
|
return nil
|
|
}
|
|
|
|
// TODO: Write godoc comment for ExecuteCommand method
|
|
func (server *EchoVault) ExecuteCommand(command []string) ([]byte, error) {
|
|
return server.handleCommand(server.context, internal.EncodeCommand(command), nil, false, true)
|
|
}
|
|
|
|
// TODO: Write godoc commend for RemoveCommand method
|
|
func (server *EchoVault) RemoveCommand(command ...string) {
|
|
switch len(command) {
|
|
case 1:
|
|
// Remove command
|
|
server.commands = slices.DeleteFunc(server.commands, func(c internal.Command) bool {
|
|
return strings.EqualFold(c.Command, command[0])
|
|
})
|
|
case 2:
|
|
// Remove subcommand
|
|
for i := 0; i < len(server.commands); i++ {
|
|
if !strings.EqualFold(server.commands[i].Command, command[0]) {
|
|
continue
|
|
}
|
|
if server.commands[i].SubCommands != nil && len(server.commands[i].SubCommands) > 0 {
|
|
server.commands[i].SubCommands = slices.DeleteFunc(server.commands[i].SubCommands, func(sc internal.SubCommand) bool {
|
|
return strings.EqualFold(sc.Command, command[1])
|
|
})
|
|
}
|
|
}
|
|
}
|
|
}
|