package main import ( "bufio" "errors" "fmt" "io" "log" "net" "strings" ) type RedisCmd struct { cmd string key string val string } type CommandExec struct { *RedisCmd Result chan string } func getCommand(reader io.Reader) (string, error) { scanner := bufio.NewScanner(reader) for scanner.Scan() { str := scanner.Text() return str, nil } return "", scanner.Err() } func isCommand(str string) bool { switch str { case "GET": fallthrough case "SET": fallthrough case "DEL": return true default: return false } } func parseCmd(line string) (*RedisCmd, error) { redisCmd := new(RedisCmd) var err error parts := strings.Split(line, " ") if len(parts) != 2 && len(parts) != 3 { err = errors.New("Invalid number of arguments") } else { for i, part := range parts { switch i { case 0: if isCommand(part) { redisCmd.cmd = part } else { err = errors.New("Unknown command") break } case 1: redisCmd.key = part case 2: if redisCmd.cmd == "SET" { redisCmd.val = part } else { err = errors.New("Invalid number of arguments") } } } } return redisCmd, err } func respond(writer io.Writer, ret string) { _, err := io.WriteString(writer, ret+"\n") if err != nil { log.Println(err) } } func main() { db := make(map[string]string) ln, err := net.Listen("tcp", ":9000") if err != nil { panic(err) } defer ln.Close() commands := make(chan CommandExec) go func() { for exec := range commands { switch exec.cmd { case "DEL": log.Println("Deleting", exec.key) delete(db, exec.key) close(exec.Result) case "GET": log.Println("Getting", exec.key) ret, ok := db[exec.key] if ok { log.Println("Value found", ret) go func() { exec.Result <- ret }() } else { log.Println("Unknown key", exec.key) close(exec.Result) } case "SET": log.Println("Setting", exec.key) db[exec.key] = exec.val close(exec.Result) } } }() for { conn, err := ln.Accept() if err != nil { panic(err) } results := make(chan string) go func() { ret, ok := <-results if ok { respond(conn, ret) } defer conn.Close() }() go func() { exec := new(CommandExec) exec.Result = results line, err := getCommand(conn) if err != nil { go func() { exec.Result <- fmt.Sprint(err) }() } cmd, err := parseCmd(line) if err != nil { go func() { exec.Result <- fmt.Sprint(err) }() } exec.RedisCmd = cmd commands <- *exec }() } }