diff --git a/internal/command/command.go b/internal/command/command.go index cf209a5..c1da23d 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -24,6 +24,8 @@ func Parse(args [][]byte) (redis.Cmd, error) { // server case "command": return server.ParseOK(b) + case "config": + return server.ParseConfig(b) case "dbsize": return server.ParseDBSize(b) case "flushdb": diff --git a/internal/command/server/config.go b/internal/command/server/config.go new file mode 100644 index 0000000..036f375 --- /dev/null +++ b/internal/command/server/config.go @@ -0,0 +1,49 @@ +package server + +import ( + "github.com/nalgeon/redka/internal/redis" +) + +// Container command for runtime configuration commands. +// CONFIG +// https://redis.io/commands/config +type Config struct { + redis.BaseCmd + subcmd string + get ConfigGet +} + +func ParseConfig(b redis.BaseCmd) (Config, error) { + // Extract the subcommand. + cmd := Config{BaseCmd: b} + if len(cmd.Args()) == 0 { + return Config{}, redis.ErrInvalidArgNum + } + cmd.subcmd = string(cmd.Args()[0]) + + // Parse the subcommand. + var err error + args := cmd.Args()[1:] + switch cmd.subcmd { + case "get": + cmd.get, err = ParseConfigGet(args) + default: + err = redis.ErrUnknownSubcmd + } + + // Return the resulting command. + if err != nil { + return Config{}, err + } + return cmd, nil +} + +func (c Config) Run(w redis.Writer, red redis.Redka) (any, error) { + switch c.subcmd { + case "get": + return c.get.Run(w, red) + default: + w.WriteString("OK") + return true, nil + } +} diff --git a/internal/command/server/config_test.go b/internal/command/server/config_test.go new file mode 100644 index 0000000..d469906 --- /dev/null +++ b/internal/command/server/config_test.go @@ -0,0 +1,63 @@ +package server + +import ( + "testing" + + "github.com/nalgeon/redka/internal/redis" + "github.com/nalgeon/redka/internal/testx" +) + +func TestConfigParse(t *testing.T) { + tests := []struct { + cmd string + want Config + err error + }{ + { + cmd: "config", + want: Config{}, + err: redis.ErrInvalidArgNum, + }, + { + cmd: "config get", + want: Config{}, + err: redis.ErrInvalidArgNum, + }, + { + cmd: "config get *", + want: Config{subcmd: "get"}, + err: nil, + }, + { + cmd: "config set parameter value", + want: Config{}, + err: redis.ErrUnknownSubcmd, + }, + } + + for _, test := range tests { + t.Run(test.cmd, func(t *testing.T) { + cmd, err := redis.Parse(ParseConfig, test.cmd) + testx.AssertEqual(t, err, test.err) + if err == nil { + testx.AssertEqual(t, cmd.subcmd, test.want.subcmd) + } else { + testx.AssertEqual(t, cmd, test.want) + } + }) + } +} + +func TestConfigExec(t *testing.T) { + t.Run("config get", func(t *testing.T) { + db, red := getDB(t) + defer db.Close() + + cmd := redis.MustParse(ParseConfig, "config get *") + conn := redis.NewFakeConn() + res, err := cmd.Run(conn, red) + testx.AssertNoErr(t, err) + testx.AssertEqual(t, res, true) + testx.AssertEqual(t, conn.Out(), "2,databases,1") + }) +} diff --git a/internal/command/server/configget.go b/internal/command/server/configget.go new file mode 100644 index 0000000..25c7c60 --- /dev/null +++ b/internal/command/server/configget.go @@ -0,0 +1,30 @@ +package server + +import ( + "github.com/nalgeon/redka/internal/redis" +) + +// Returns the effective values of configuration parameters. +// CONFIG GET parameter [parameter ...] +// https://redis.io/commands/config-get +type ConfigGet struct { + params []string +} + +func ParseConfigGet(args [][]byte) (ConfigGet, error) { + if len(args) < 1 { + return ConfigGet{}, redis.ErrInvalidArgNum + } + cmd := ConfigGet{params: make([]string, len(args))} + for i, arg := range args { + cmd.params[i] = string(arg) + } + return cmd, nil +} + +func (c ConfigGet) Run(w redis.Writer, _ redis.Redka) (any, error) { + w.WriteArray(2) + w.WriteString("databases") + w.WriteInt(1) + return true, nil +}