package main import ( "errors" "net" "path/filepath" "redisClone/pkg/core" "sort" "strconv" "time" ) func handleGet(args []string)interface{} { if len(args) != 2 { err := errors.New("wrong number of arguments for 'GET'") return err } key := args[1] result := db.ExecuteRead(key, func(s *core.Shard) interface{}{ item, exists := s.Data[key] if !exists { return nil } if item.ExpiresAt != nil && time.Now().After(*item.ExpiresAt) { delete(s.Data, key) return nil } strValue, ok := item.Value.(string) if !ok { err := errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") return err } return strValue }) return result } func handleExists(args []string)interface{} { if len(args) != 2 { err := errors.New("wrong number of arguments for 'EXISTS'") return err } key := args[1] result := db.ExecuteRead(key, func(s *core.Shard) interface{}{ _, exists := s.Data[key] if exists { return 1 } else { return 0 } }) return result } func handleSet(args []string)interface{} { if len(args) != 3 { err := errors.New("wrong number of arguments for 'SET'") return err } key := args[1] result := db.Execute(key, func(s *core.Shard) interface{}{ item := core.Item{Value: args[2]} s.Data[key] = item return core.SimpleString("OK") }) return result } func handleDel(args []string)interface{} { if len(args) < 2 { err := errors.New("wrong number of arguments for 'DEL'") return err } keys := args[1:] deletedKeys := db.ExecuteMulti(keys, func(shards []*core.Shard) interface{}{ count := 0 for _, key := range keys{ shard := db.GetShard(key) _, exists := shard.Data[key] if exists { delete(shard.Data, key) count += 1 } } return count }) return deletedKeys } func handleIncr(args []string)interface{} { if len(args) != 2 { err := errors.New("wrong number of arguments for 'INCR'") return err } key := args[1] result := db.Execute(key, func(s *core.Shard)interface{}{ item, exists := s.Data[key] if exists { strValue, ok := item.Value.(string) if !ok { err := errors.New("value is not an integer or out of range") return err } currentInt, err := strconv.Atoi(strValue) if err != nil { err := errors.New("value is not an integer or out of range") return err } newValueStr := strconv.Itoa(currentInt + 1) s.Data[key] = core.Item{Value: newValueStr} return currentInt + 1 } else { s.Data[key] = core.Item{Value: "1"} return 1 } }) return result } func handleDecr(args []string)interface{} { if len(args) != 2 { err := errors.New("wrong number of arguments for 'DECR'") return err } key := args[1] result := db.Execute(key, func(s *core.Shard)interface{}{ item, exists := s.Data[key] if exists { strValue, ok := item.Value.(string) if !ok { err := errors.New("value is not an integer or out of range") return err } currentInt, err := strconv.Atoi(strValue) if err != nil { err := errors.New("value is not an integer or out of range") return err } newValueStr := strconv.Itoa(currentInt + 1) s.Data[key] = core.Item{Value: newValueStr} return currentInt - 1 } else { s.Data[key] = core.Item{Value: "1"} return -1 } }) return result } func handlePing(conn net.Conn, args []string) { conn.Write(core.SerializeRESP(core.SimpleString("OK"))) } func handleFlushall(args []string)interface{} { if len(args) != 1 { err := errors.New("wrong number of arguments for 'FLUSHALL'") return err } result := db.ExecuteAll(func(shards []*core.Shard)interface{}{ for _, shard := range shards{ shard.Data = make(map[string]core.Item) } return core.SimpleString("OK") }) return result } func handleRename(args []string)interface{}{ if len(args) != 3 { err := errors.New("wrong number of arguments for 'RENAME'") return err } keys := args[1:] result := db.ExecuteMulti(keys,func(shards []*core.Shard)interface{}{ item, exists := db.GetShard(keys[0]).Data[keys[0]] if !exists { return errors.New("no such key") } db.GetShard(keys[1]).Data[keys[1]] = item delete(db.GetShard(keys[0]).Data, keys[0]) return core.SimpleString("OK") }) return result } func handleRpush(args []string)interface{} { if len(args) < 3 { err := errors.New("wrong number of arguments for 'RPUSH'") return err } key := args[1] newItems := args[2:] result := db.Execute(key, func(s *core.Shard) interface{} { var list []string existingItem, exists := s.Data[key] if exists { var ok bool list, ok = existingItem.Value.([]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } } else { list = []string{} } list = append(list, newItems...) s.Data[key] = core.Item{Value: list} return len(list) }) return result } func handleLpush(args []string)interface{} { if len(args) < 3 { err := errors.New("wrong number of arguments for 'LPUSH'") return err } key := args[1] newItems := args[2:] result := db.Execute(key, func(s *core.Shard) interface{} { var list []string existingItem, exists := s.Data[key] if exists { var ok bool list, ok = existingItem.Value.([]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } } else { list = []string{} } list = append(newItems, list...) s.Data[key] = core.Item{Value: list} return len(list) }) return result } func handleLrange(args []string)interface{} { if len(args) != 4 { err := errors.New("wrong number of arguments for 'LRANGE'") return err } start, err1 := strconv.Atoi(args[2]) stop, err2 := strconv.Atoi(args[3]) if err1 != nil || err2 != nil { err := errors.New("value is not an integer or out of range") return err } key := args[1] result := db.ExecuteRead(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] if !exists { return []string{} } if item.ExpiresAt != nil && time.Now().After(*item.ExpiresAt) { delete(s.Data, key) return nil } typedList, ok := item.Value.([]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } size := len(typedList) if start < 0 { start += size if start < 0 { start = 0 } } if stop < 0 { stop += size if stop < 0 { stop = 0 } } if stop >= size { stop = size - 1 } if start > stop || start >= size { return []string{} } return typedList[start : stop+1] }) return result } func handleLpop(args []string)interface{} { if len(args) != 2 { err := errors.New("wrong number of arguments for 'LPOP'") return err } key := args[1] result := db.Execute(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] if !exists { return nil } typedList, ok := item.Value.([]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } if len(typedList) == 0 { return nil } newItem := typedList[0] newList := typedList[1:] if len(newList) <= 0 { delete(s.Data, key) } else { s.Data[key] = core.Item{Value: newList} } return newItem }) return result } func handleRpop(args []string)interface{} { if len(args) != 2 { err := errors.New("wrong number of arguments for 'RPOP'") return err } key := args[1] result := db.Execute(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] if !exists { return nil } typedList, ok := item.Value.([]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } if len(typedList) == 0 { return nil } newItem := typedList[len(typedList)-1] newList := typedList[:len(typedList)-1] if len(newList) <= 0 { delete(s.Data, key) } else { s.Data[key] = core.Item{Value: newList} } return newItem }) return result } func handleExpire(args []string)interface{} { if len(args) != 3 { return errors.New("wrong number of arguments for 'EXPIRE'") } key := args[1] seconds, err := strconv.Atoi(args[2]) if err != nil { return errors.New("value is not an integer") } result := db.Execute(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] if !exists { return 0 } expiry := time.Now().Add(time.Duration(seconds) * time.Second) item.ExpiresAt = &expiry s.Data[key] = item return 1 }) return result } func handleHset(args []string)interface{}{ if len(args) < 4 || (len(args)-2)%2 != 0 { err := errors.New("wrong number of arguments for 'HSET'") return err } key := args[1] result := db.Execute(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] var hash map[string]string if exists { var ok bool hash, ok = item.Value.(map[string]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } } else { hash = make(map[string]string) } count := 0 for i := 2; i < len(args); i += 2 { hash[args[i]] = args[i+1] count++ } s.Data[key] = core.Item{Value: hash} return count }) return result } func handleHget(args []string)interface{} { if len(args) != 3 { return errors.New("wrong number of arguments for 'HGET'") } key := args[1] field := args[2] result := db.ExecuteRead(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] if !exists { return nil } hash, ok := item.Value.(map[string]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } val, found := hash[field] if !found { return nil } return val }) return result } func handleHgetall(args []string)interface{} { if len(args) != 2 { return errors.New("wrong number of arguments for 'HGETALL'") } key := args[1] result := db.ExecuteRead(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] if !exists { return nil } hash, ok := item.Value.(map[string]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } return hash }) return result } func handleHkeys(args []string)interface{} { if len(args) != 2 { return errors.New("wrong number of arguments for 'HKEYS'") } key := args[1] result := db.ExecuteRead(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] if !exists { return nil } hash, ok := item.Value.(map[string]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } keys := make([]string, 0, len(hash)) for k := range hash { keys = append(keys, k) } return keys }) return result } func handleHvalues(args []string)interface{} { if len(args) != 2 { return errors.New("wrong number of arguments for 'HVALUES'") } key := args[1] result := db.ExecuteRead(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] if !exists { return nil } hash, ok := item.Value.(map[string]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } values := make([]string, 0, len(hash)) for _, v := range hash { values = append(values, v) } return values }) return result } func handleType(args []string)interface{} { if len(args) != 2 { return errors.New("wrong number of arguments for 'TYPE'") } key := args[1] result := db.ExecuteRead(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] if !exists { return core.SimpleString("none") } switch item.Value.(type) { case string: return core.SimpleString("string") case []string: return core.SimpleString("list") case map[string]string: return core.SimpleString("hash") default: return core.SimpleString("unknown") } }) return result } func handleDbsize(args []string)interface{} { if len(args) != 1 { return errors.New("wrong number of arguments for 'DBSIZE'") } result := db.ExecuteReadAll(func(shards []*core.Shard) interface{} { count := 0 for _, shard := range shards { count += len(shard.Data) } return count }) return result } func handleKeys(args []string)interface{} { if len(args) < 2 || len(args) > 3 { return errors.New("wrong number of arguments for 'KEYS'") } pattern := args[1] limit := -1 if len(args) == 3 { val, err := strconv.Atoi(args[2]) if err != nil { return errors.New("value is not an integer") } limit = val } result := db.ExecuteReadAll(func(shards []*core.Shard) interface{} { var matches []string for _, shard := range shards { for key := range shard.Data { matched, err := filepath.Match(pattern, key) if err != nil { return errors.New("illegal glob pattern") } if matched { matches = append(matches, key) } } } sort.Strings(matches) if limit != -1 && len(matches) > limit { matches = matches[:limit] } return matches }) return result } func handleLrem(args []string)interface{} { if len(args) != 4 { return errors.New("wrong number of arguments for 'LREM'") } key := args[1] count, err := strconv.Atoi(args[2]) toBeRemoved := args[3] if err != nil { return errors.New("value is not an integer") } result := db.Execute(key, func(s *core.Shard) interface{} { item, exists := s.Data[key] if !exists { return 0 } list, ok := item.Value.([]string) if !ok { return errors.New("WRONGTYPE Operation against a key holding the wrong kind of value") } counter := 0 absCount := count if count < 0 { absCount = -count } var newList []string if count == 0 { newList = []string{} for _, value := range list { if value == toBeRemoved { counter++ } else { newList = append(newList, value) } } } else if count > 0 { newList = []string{} for _, value := range list { if value == toBeRemoved && counter < absCount { counter++ } else { newList = append(newList, value) } } } else { // count < 0 reversedNewList := []string{} for i := len(list) - 1; i >= 0; i-- { value := list[i] if value == toBeRemoved && counter < absCount { counter++ } else { reversedNewList = append(reversedNewList, value) } } newList = make([]string, len(reversedNewList)) for i, v := range reversedNewList { newList[len(reversedNewList)-1-i] = v } } if len(newList) == 0 { delete(s.Data, key) } else { item.Value = newList s.Data[key] = item } return counter }) return result }