package main import ( "bufio" "fmt" "io" "net" "os" "strconv" "strings" "sync" ) type RedisDB struct { mu sync.Mutex data map[string]any } var db RedisDB func main() { db = RedisDB{data: make(map[string]any)} listener, err := net.Listen("tcp", ":6379") if err != nil { fmt.Println("error starting server: ", err) os.Exit(1) } defer listener.Close() fmt.Println("Redis clone listening on port 6379...") for { conn, err := listener.Accept() if err != nil { fmt.Println("Error accepting connection:", err) continue } go handleConnection(conn) } } func handleGet(conn net.Conn, key string) { db.mu.Lock() defer db.mu.Unlock() value, exists := db.data[key] if !exists { conn.Write([]byte("$-1\r\n")) return } strValue, ok := value.(string) if !ok { conn.Write([]byte("-WRONGTYPE Operation against a key holding the wrong kind of value\r\n")) return } conn.Write([]byte("+" + strValue + "\r\n")) } func handleExists(conn net.Conn, key string) { db.mu.Lock() defer db.mu.Unlock() _, exists := db.data[key] if exists { conn.Write([]byte(":1\r\n")) } else { conn.Write([]byte(":0\r\n")) } } func handleSet(conn net.Conn, key, value string) { db.mu.Lock() defer db.mu.Unlock() db.data[key] = value conn.Write([]byte("+OK\r\n")) } func handleDel(conn net.Conn, key string) { db.mu.Lock() defer db.mu.Unlock() _, exists := db.data[key] if exists { delete(db.data, key) conn.Write([]byte(":1\r\n")) } else { conn.Write([]byte(":0\r\n")) } } func handleIncr(conn net.Conn, key string) { db.mu.Lock() defer db.mu.Unlock() value, exists := db.data[key] if exists { strValue, ok := value.(string) if !ok { conn.Write([]byte("-ERR value is not an integer or out of range\r\n")) return } currentInt, err := strconv.Atoi(strValue) if err != nil { conn.Write([]byte("-ERR value is not an integer or out of range\r\n")) return } newValueStr := strconv.Itoa(currentInt + 1) db.data[key] = newValueStr conn.Write([]byte(":" + newValueStr + "\r\n")) } else { db.data[key] = "1" conn.Write([]byte(":1\r\n")) } } func handleDecr(conn net.Conn, key string) { db.mu.Lock() defer db.mu.Unlock() value, exists := db.data[key] if exists { strValue, ok := value.(string) if !ok { conn.Write([]byte("-ERR value is not an integer or out of range\r\n")) return } currentInt, err := strconv.Atoi(strValue) if err != nil { conn.Write([]byte("-ERR value is not an integer or out of range\r\n")) return } newValueStr := strconv.Itoa(currentInt - 1) db.data[key] = newValueStr conn.Write([]byte(":" + newValueStr + "\r\n")) } else { db.data[key] = "-1" conn.Write([]byte(":-1\r\n")) } } func handlePing(conn net.Conn) { conn.Write([]byte("+PONG\r\n")) } func handleFlushall(conn net.Conn) { db.mu.Lock() defer db.mu.Unlock() db.data = make(map[string]any) conn.Write([]byte("+OK\r\n")) } func handleRpush(conn net.Conn, args []string) { key := args[1] newItems := args[2:] db.mu.Lock() defer db.mu.Unlock() var list []string existingValue, exists := db.data[key] if exists { var ok bool list, ok = existingValue.([]string) if !ok { conn.Write([]byte("-WRONGTYPE Operation against a key holding the wrong kind of value\r\n")) return } } else { list = []string{} } list = append(list, newItems...) db.data[key] = list conn.Write([]byte(":" + strconv.Itoa(len(list)) + "\r\n")) } func parseRESP(reader *bufio.Reader) ([]string, error) { line, err := reader.ReadString('\n') if err != nil { return nil, err } if len(line) == 0 { return nil, fmt.Errorf("empty line") } switch line[0] { case '*': var args []string argC, _ := strconv.Atoi(strings.TrimSpace(line[1:])) for i := 0; i < argC; i++ { nestedArgs, err := parseRESP(reader) if err != nil { return nil, err } args = append(args, nestedArgs...) } return args, nil case '$': strLen, _ := strconv.Atoi(strings.TrimSpace(line[1:])) if strLen == -1 { return []string{""}, nil } buf := make([]byte, strLen) _, err = io.ReadFull(reader, buf) if err != nil { return nil, err } _, err = reader.Discard(2) if err != nil { return nil, err } return []string{string(buf)}, nil case '+': cleanStr := strings.TrimSpace(line[1:]) return []string{cleanStr}, nil case ':': return []string{strings.TrimSpace(line[1:])}, nil default: return nil, fmt.Errorf("unknown command type: %c", line[0]) } } func handleConnection(conn net.Conn) { defer conn.Close() fmt.Println("Client connected:", conn.RemoteAddr()) reader := bufio.NewReader(conn) for { args, err := parseRESP(reader) if err != nil { fmt.Println("Client disconnected or error parsing:", conn.RemoteAddr(), err) return } if len(args) == 0 { continue } fmt.Printf("Parsed RESP Arguments: %v\n", args) command := strings.ToUpper(args[0]) switch { case command == "GET" && len(args) == 2: handleGet(conn, args[1]) case command == "SET" && len(args) == 3: handleSet(conn, args[1], args[2]) case command == "EXISTS" && len(args) == 2: handleExists(conn, args[1]) case command == "DEL" && len(args) == 2: handleDel(conn, args[1]) case command == "INCR" && len(args) == 2: handleIncr(conn, args[1]) case command == "DECR" && len(args) == 2: handleDecr(conn, args[1]) case command == "PING" && len(args) == 1: handlePing(conn) case command == "FLUSHALL" && len(args) == 1: handleFlushall(conn) case command == "RPUSH" && len(args) >= 3: handleRpush(conn, args) default: conn.Write([]byte("-ERR unknown command or wrong arguments\r\n")) } } }