// SCARD func (m *Miniredis) cmdScard(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 1 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } key := r.Args[0] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(key) { out.WriteZero() return } if db.t(key) != "set" { out.WriteErrorString(ErrWrongType.Error()) return } members := db.setMembers(key) out.WriteInt(len(members)) }) }
// RPOP func (m *Miniredis) cmdRpop(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 1 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } key := r.Args[0] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(key) { out.WriteNil() return } if db.t(key) != "list" { out.WriteErrorString(msgWrongType) return } elem := db.listPop(key) out.WriteString(elem) }) }
// RPUSH func (m *Miniredis) cmdRpush(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) < 2 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } key := r.Args[0] args := r.Args[1:] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if db.exists(key) && db.t(key) != "list" { out.WriteErrorString(msgWrongType) return } var newLen int for _, value := range args { newLen = db.listPush(key, value) } out.WriteInt(newLen) }) }
// HINCRBYFLOAT func (m *Miniredis) cmdHincrbyfloat(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 3 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'hincrbyfloat' command") return nil } key := r.Args[0] field := r.Args[1] delta, err := strconv.ParseFloat(r.Args[2], 64) if err != nil { setDirty(r.Client()) out.WriteErrorString(msgInvalidFloat) return nil } return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if t, ok := db.keys[key]; ok && t != "hash" { out.WriteErrorString(msgWrongType) return } v, err := db.hashIncrfloat(key, field, delta) if err != nil { out.WriteErrorString(err.Error()) return } out.WriteString(formatFloat(v)) }) }
// MMSET func (m *Miniredis) cmdHmset(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) < 3 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'hmset' command") return nil } key := r.Args[0] args := r.Args[1:] if len(args)%2 != 0 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for HMSET") return nil } return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if t, ok := db.keys[key]; ok && t != "hash" { out.WriteErrorString(msgWrongType) return } for len(args) > 0 { field := args[0] value := args[1] args = args[2:] db.hashSet(key, field, value) } out.WriteOK() }) }
// HKEYS func (m *Miniredis) cmdHkeys(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 1 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'hkeys' command") return nil } key := r.Args[0] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(key) { out.WriteBulkLen(0) return } if db.t(key) != "hash" { out.WriteErrorString(msgWrongType) return } fields := db.hashFields(key) out.WriteBulkLen(len(fields)) for _, f := range fields { out.WriteString(f) } }) }
// HSET func (m *Miniredis) cmdHset(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 3 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'hset' command") return nil } key := r.Args[0] field := r.Args[1] value := r.Args[2] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if t, ok := db.keys[key]; ok && t != "hash" { out.WriteErrorString(msgWrongType) return } if db.hashSet(key, field, value) { out.WriteZero() } else { out.WriteOne() } }) }
// MGET func (m *Miniredis) cmdMget(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) < 1 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) out.WriteBulkLen(len(r.Args)) for _, k := range r.Args { if t, ok := db.keys[k]; !ok || t != "string" { out.WriteNil() continue } v, ok := db.stringKeys[k] if !ok { // Should not happen, we just checked keys[] out.WriteNil() continue } out.WriteString(v) } }) }
// DECR func (m *Miniredis) cmdDecr(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 1 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) key := r.Args[0] if t, ok := db.keys[key]; ok && t != "string" { out.WriteErrorString(msgWrongType) return } v, err := db.stringIncr(key, -1) if err != nil { out.WriteErrorString(err.Error()) return } // Don't touch TTL out.WriteInt(v) }) }
// PING func (m *Redico) cmdPing(out *redeo.Responder, r *redeo.Request) error { if !m.handleAuth(r.Client(), out) { return nil } out.WriteInlineString("PONG") return nil }
// GETSET func (m *Miniredis) cmdGetset(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 2 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } key := r.Args[0] value := r.Args[1] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if t, ok := db.keys[key]; ok && t != "string" { out.WriteErrorString(msgWrongType) return } old, ok := db.stringKeys[key] db.stringSet(key, value) // a GETSET clears the expire delete(db.expire, key) if !ok { out.WriteNil() return } out.WriteString(old) return }) }
// LPUSHX func (m *Miniredis) cmdLpushx(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 2 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'lpushx' command") return nil } key := r.Args[0] value := r.Args[1] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(key) { out.WriteZero() return } if db.t(key) != "list" { out.WriteErrorString(msgWrongType) return } newLen := db.listLpush(key, value) out.WriteInt(newLen) }) }
// LPOP func (m *Miniredis) cmdLpop(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 1 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'lpop' command") return nil } key := r.Args[0] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(key) { // Non-existing key is fine. out.WriteNil() return } if db.t(key) != "list" { out.WriteErrorString(msgWrongType) return } elem := db.listLpop(key) out.WriteString(elem) }) }
// SDIFF func (m *Miniredis) cmdSdiff(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) < 1 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } keys := r.Args return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) set, err := db.setDiff(keys) if err != nil { out.WriteErrorString(err.Error()) return } out.WriteBulkLen(len(set)) for k := range set { out.WriteString(k) } }) }
// SCARD func (m *Miniredis) cmdScard(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 1 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'scard' command") return nil } key := r.Args[0] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(key) { out.WriteZero() return } if db.t(key) != "set" { out.WriteErrorString(ErrWrongType.Error()) return } members := db.setMembers(key) out.WriteInt(len(members)) }) }
// APPEND func (m *Miniredis) cmdAppend(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 2 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } key := r.Args[0] value := r.Args[1] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if t, ok := db.keys[key]; ok && t != "string" { out.WriteErrorString(msgWrongType) return } newValue := db.stringKeys[key] + value db.stringSet(key, newValue) out.WriteInt(len(newValue)) }) }
// HEXISTS func (m *Miniredis) cmdHexists(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 2 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'hexists' command") return nil } key := r.Args[0] field := r.Args[1] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) t, ok := db.keys[key] if !ok { out.WriteInt(0) return } if t != "hash" { out.WriteErrorString(msgWrongType) return } if _, ok := db.hashKeys[key][field]; !ok { out.WriteInt(0) return } out.WriteInt(1) }) }
// MOVE func (m *Miniredis) cmdMove(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 2 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } key := r.Args[0] targetDB, err := strconv.Atoi(r.Args[1]) if err != nil { targetDB = 0 } return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { if ctx.selectedDB == targetDB { out.WriteErrorString("ERR source and destination objects are the same") return } db := m.db(ctx.selectedDB) targetDB := m.db(targetDB) if !db.move(key, targetDB) { out.WriteZero() return } out.WriteOne() }) }
// HVALS func (m *Miniredis) cmdHvals(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 1 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'hvals' command") return nil } key := r.Args[0] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) t, ok := db.keys[key] if !ok { out.WriteBulkLen(0) return } if t != "hash" { out.WriteErrorString(msgWrongType) return } out.WriteBulkLen(len(db.hashKeys[key])) for _, v := range db.hashKeys[key] { out.WriteString(v) } }) }
// RANDOMKEY func (m *Miniredis) cmdRandomkey(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 0 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if len(db.keys) == 0 { out.WriteNil() return } nr := rand.Intn(len(db.keys)) for k := range db.keys { if nr == 0 { out.WriteString(k) return } nr-- } }) }
// HMGET func (m *Miniredis) cmdHmget(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) < 2 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'hmget' command") return nil } key := r.Args[0] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if t, ok := db.keys[key]; ok && t != "hash" { out.WriteErrorString(msgWrongType) return } f, ok := db.hashKeys[key] if !ok { f = map[string]string{} } out.WriteBulkLen(len(r.Args) - 1) for _, k := range r.Args[1:] { v, ok := f[k] if !ok { out.WriteNil() continue } out.WriteString(v) } }) }
// RENAMENX func (m *Miniredis) cmdRenamenx(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 2 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } from := r.Args[0] to := r.Args[1] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(from) { out.WriteErrorString(msgKeyNotFound) return } if db.exists(to) { out.WriteZero() return } db.rename(from, to) out.WriteOne() }) }
// HSETNX func (m *Miniredis) cmdHsetnx(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 3 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'hsetnx' command") return nil } key := r.Args[0] field := r.Args[1] value := r.Args[2] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if t, ok := db.keys[key]; ok && t != "hash" { out.WriteErrorString(msgWrongType) return } if _, ok := db.hashKeys[key]; !ok { db.hashKeys[key] = map[string]string{} db.keys[key] = "hash" } _, ok := db.hashKeys[key][field] if ok { out.WriteZero() return } db.hashKeys[key][field] = value db.keyVersion[key]++ out.WriteOne() }) }
// TTL func (m *Miniredis) cmdTTL(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 1 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } key := r.Args[0] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if _, ok := db.keys[key]; !ok { // No such key out.WriteInt(-2) return } value, ok := db.expire[key] if !ok { // No expire value out.WriteInt(-1) return } out.WriteInt(value) }) }
// LLEN func (m *Miniredis) cmdLlen(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 1 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } key := r.Args[0] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) t, ok := db.keys[key] if !ok { // No such key. That's zero length. out.WriteZero() return } if t != "list" { out.WriteErrorString(msgWrongType) return } out.WriteInt(len(db.listKeys[key])) }) }
// SISMEMBER func (m *Miniredis) cmdSismember(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 2 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'sismember' command") return nil } key := r.Args[0] value := r.Args[1] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(key) { out.WriteZero() return } if db.t(key) != "set" { out.WriteErrorString(ErrWrongType.Error()) return } if db.setIsMember(key, value) { out.WriteOne() return } out.WriteZero() }) }
// RPOPLPUSH func (m *Miniredis) cmdRpoplpush(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 2 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } src := r.Args[0] dst := r.Args[1] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(src) { out.WriteNil() return } if db.t(src) != "list" || (db.exists(dst) && db.t(dst) != "list") { out.WriteErrorString(msgWrongType) return } elem := db.listPop(src) db.listLpush(dst, elem) out.WriteString(elem) }) }
// SREM func (m *Miniredis) cmdSrem(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) < 2 { setDirty(r.Client()) out.WriteErrorString("ERR wrong number of arguments for 'srem' command") return nil } key := r.Args[0] fields := r.Args[1:] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(key) { out.WriteInt(0) return } if db.t(key) != "set" { out.WriteErrorString(ErrWrongType.Error()) return } out.WriteInt(db.setRem(key, fields...)) }) }
// RPUSHX func (m *Miniredis) cmdRpushx(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) != 2 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } key := r.Args[0] value := r.Args[1] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) if !db.exists(key) { out.WriteZero() return } if db.t(key) != "list" { out.WriteErrorString(msgWrongType) return } newLen := db.listPush(key, value) out.WriteInt(newLen) }) }
// SUNIONSTORE func (m *Miniredis) cmdSunionstore(out *redeo.Responder, r *redeo.Request) error { if len(r.Args) < 2 { setDirty(r.Client()) return r.WrongNumberOfArgs() } if !m.handleAuth(r.Client(), out) { return nil } dest := r.Args[0] keys := r.Args[1:] return withTx(m, out, r, func(out *redeo.Responder, ctx *connCtx) { db := m.db(ctx.selectedDB) set, err := db.setUnion(keys) if err != nil { out.WriteErrorString(err.Error()) return } db.del(dest, true) db.setSet(dest, set) out.WriteInt(len(set)) }) }