Skip to content

Commit

Permalink
feat: add dynamic fields handler to INFO command
Browse files Browse the repository at this point in the history
  • Loading branch information
mhughdo committed Sep 9, 2024
1 parent 5eabf27 commit c22d36a
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 16 deletions.
6 changes: 6 additions & 0 deletions app/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"log"
"os"
"os/signal"
"strings"
"syscall"
"time"

Expand All @@ -20,6 +21,7 @@ var (
port = flag.String("port", "6379", "listening port")
dir = flag.String("dir", "/tmp/redis", "data directory")
dbFilename = flag.String("dbfilename", "dump.rdb", "database filename")
replicaOf = flag.String("replicaof", "", "replica of another redis server")
)

func run(ctx context.Context, _ io.Writer, _ []string) error {
Expand All @@ -30,10 +32,14 @@ func run(ctx context.Context, _ io.Writer, _ []string) error {
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)
defer signal.Stop(sigCh)
cfg := config.NewConfig()
if *replicaOf != "" && len(strings.Split(*replicaOf, " ")) != 2 {
return fmt.Errorf("invalid replicaof flag, expected format: replicaof <host> <port>")
}
err := cfg.SetBatch(map[string]string{
config.ListenAddrKey: fmt.Sprintf(":%s", *port),
config.DirKey: *dir,
config.DBFilenameKey: *dbFilename,
config.ReplicaOfKey: *replicaOf,
})
if err != nil {
return err
Expand Down
5 changes: 5 additions & 0 deletions internal/app/server/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ const (
ListenAddrKey = "LISTENADDR"
DirKey = "DIR"
DBFilenameKey = "DBFILENAME"
ReplicaOfKey = "REPLICAOF"
)

var supportedOptions = map[string]struct{}{
ListenAddrKey: {},
DirKey: {},
DBFilenameKey: {},
ReplicaOfKey: {},
}

type Config struct {
Expand Down Expand Up @@ -52,6 +54,9 @@ func (c *Config) SetBatch(pairs map[string]string) error {
if _, exists := supportedOptions[strings.ToUpper(key)]; !exists {
return fmt.Errorf("%s for %s", ErrOptionNotFound, key)
}
if value == "" {
continue
}
c.options[key] = value
}
return nil
Expand Down
2 changes: 1 addition & 1 deletion pkg/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func NewCommandFactory(kv keyval.KV, cfg *config.Config) *CommandFactory {
"set": &Set{kv: kv},
"get": &Get{kv: kv},
"hello": &Hello{},
"info": &Info{},
"info": &Info{cfg: cfg},
"client": &ClientCmd{},
"config": &ConfigCmd{cfg: cfg},
"keys": &Keys{kv: kv},
Expand Down
61 changes: 46 additions & 15 deletions pkg/command/info.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"strings"

"github.com/codecrafters-io/redis-starter-go/internal/app/server/config"
"github.com/codecrafters-io/redis-starter-go/internal/client"
"github.com/codecrafters-io/redis-starter-go/pkg/resp"
)
Expand All @@ -13,52 +14,82 @@ const (
REPLICATION = "replication"
)

var (
sections = map[string]map[string]string{
SERVER: {
type DynamicFieldHandler func(*config.Config) string

type SectionInfo struct {
StaticFields map[string]string
DynamicFields map[string]DynamicFieldHandler
}

var sections = map[string]SectionInfo{
SERVER: {
StaticFields: map[string]string{
"src_version": "1.0.0",
"redis_version": "7.4-rc1",
"redis_git_sha1": "random-sha1",
"redis_build_id": "20240616",
"redis_mode": "standalone",
},
REPLICATION: {
"role": "master",
},
REPLICATION: {
DynamicFields: map[string]DynamicFieldHandler{
"role": determineRole,
},
}
)
},
}

type Info struct {
cfg *config.Config
}

func (h *Info) Execute(c *client.Client, wr *resp.Writer, args []*resp.Resp) error {
argsLen := len(args)
if argsLen > 1 {
return wr.WriteError(fmt.Errorf("wrong number of arguments for 'info' command"))
}

str := strings.Builder{}

if argsLen == 1 {
str.WriteString(buildSectionString(args[0].String(), sections[args[0].String()]))
return wr.WriteString(str.String())
}
for sectionName, section := range sections {
str.WriteString(buildSectionString(sectionName, section))
str.WriteString("\r\n")
sectionName := args[0].String()
if sectionInfo, exists := sections[sectionName]; exists {
str.WriteString(buildSectionString(sectionName, sectionInfo, h.cfg))
}
} else {
for sectionName, sectionInfo := range sections {
str.WriteString(buildSectionString(sectionName, sectionInfo, h.cfg))
str.WriteString("\r\n")
}
}

return wr.WriteValue(str.String())
}

func buildSectionString(sectionName string, section map[string]string) string {
func buildSectionString(sectionName string, sectionInfo SectionInfo, cfg *config.Config) string {
// # Server\r\nupstash_version:1.10.5\r\n,etc.
var sb strings.Builder
sb.WriteString(fmt.Sprintf("# %s\r\n", sectionName))
for key, value := range section {

for key, value := range sectionInfo.StaticFields {
sb.WriteString(fmt.Sprintf("%s:%s\r\n", key, value))
}

for key, handler := range sectionInfo.DynamicFields {
value := handler(cfg)
sb.WriteString(fmt.Sprintf("%s:%s\r\n", key, value))
}

return sb.String()
}

func (h *Info) IsBlocking(_ []*resp.Resp) bool {
return false
}

func determineRole(cfg *config.Config) string {
_, err := cfg.Get(config.ReplicaOfKey)
if err == nil {
return "slave"
}
return "master"
}

0 comments on commit c22d36a

Please sign in to comment.