package redis import ( "context" "time" ) type StringCmdable interface { Append(ctx context.Context, key, value string) *IntCmd Decr(ctx context.Context, key string) *IntCmd DecrBy(ctx context.Context, key string, decrement int64) *IntCmd Get(ctx context.Context, key string) *StringCmd GetRange(ctx context.Context, key string, start, end int64) *StringCmd GetSet(ctx context.Context, key string, value interface{}) *StringCmd GetEx(ctx context.Context, key string, expiration time.Duration) *StringCmd GetDel(ctx context.Context, key string) *StringCmd Incr(ctx context.Context, key string) *IntCmd IncrBy(ctx context.Context, key string, value int64) *IntCmd IncrByFloat(ctx context.Context, key string, value float64) *FloatCmd LCS(ctx context.Context, q *LCSQuery) *LCSCmd MGet(ctx context.Context, keys ...string) *SliceCmd MSet(ctx context.Context, values ...interface{}) *StatusCmd MSetNX(ctx context.Context, values ...interface{}) *BoolCmd MSetEX(ctx context.Context, args MSetEXArgs, values ...interface{}) *IntCmd Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd SetArgs(ctx context.Context, key string, value interface{}, a SetArgs) *StatusCmd SetEx(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd SetNX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd SetXX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd SetRange(ctx context.Context, key string, offset int64, value string) *IntCmd StrLen(ctx context.Context, key string) *IntCmd } func (c cmdable) Append(ctx context.Context, key, value string) *IntCmd { cmd := NewIntCmd(ctx, "append", key, value) _ = c(ctx, cmd) return cmd } func (c cmdable) Decr(ctx context.Context, key string) *IntCmd { cmd := NewIntCmd(ctx, "decr", key) _ = c(ctx, cmd) return cmd } func (c cmdable) DecrBy(ctx context.Context, key string, decrement int64) *IntCmd { cmd := NewIntCmd(ctx, "decrby", key, decrement) _ = c(ctx, cmd) return cmd } // Get Redis `GET key` command. It returns redis.Nil error when key does not exist. func (c cmdable) Get(ctx context.Context, key string) *StringCmd { cmd := NewStringCmd(ctx, "get", key) _ = c(ctx, cmd) return cmd } func (c cmdable) GetRange(ctx context.Context, key string, start, end int64) *StringCmd { cmd := NewStringCmd(ctx, "getrange", key, start, end) _ = c(ctx, cmd) return cmd } func (c cmdable) GetSet(ctx context.Context, key string, value interface{}) *StringCmd { cmd := NewStringCmd(ctx, "getset", key, value) _ = c(ctx, cmd) return cmd } // GetEx An expiration of zero removes the TTL associated with the key (i.e. GETEX key persist). // Requires Redis >= 6.2.0. func (c cmdable) GetEx(ctx context.Context, key string, expiration time.Duration) *StringCmd { args := make([]interface{}, 0, 4) args = append(args, "getex", key) if expiration > 0 { if usePrecise(expiration) { args = append(args, "px", formatMs(ctx, expiration)) } else { args = append(args, "ex", formatSec(ctx, expiration)) } } else if expiration == 0 { args = append(args, "persist") } cmd := NewStringCmd(ctx, args...) _ = c(ctx, cmd) return cmd } // GetDel redis-server version >= 6.2.0. func (c cmdable) GetDel(ctx context.Context, key string) *StringCmd { cmd := NewStringCmd(ctx, "getdel", key) _ = c(ctx, cmd) return cmd } func (c cmdable) Incr(ctx context.Context, key string) *IntCmd { cmd := NewIntCmd(ctx, "incr", key) _ = c(ctx, cmd) return cmd } func (c cmdable) IncrBy(ctx context.Context, key string, value int64) *IntCmd { cmd := NewIntCmd(ctx, "incrby", key, value) _ = c(ctx, cmd) return cmd } func (c cmdable) IncrByFloat(ctx context.Context, key string, value float64) *FloatCmd { cmd := NewFloatCmd(ctx, "incrbyfloat", key, value) _ = c(ctx, cmd) return cmd } type SetCondition string const ( // NX only set the keys and their expiration if none exist NX SetCondition = "NX" // XX only set the keys and their expiration if all already exist XX SetCondition = "XX" ) type ExpirationMode string const ( // EX sets expiration in seconds EX ExpirationMode = "EX" // PX sets expiration in milliseconds PX ExpirationMode = "PX" // EXAT sets expiration as Unix timestamp in seconds EXAT ExpirationMode = "EXAT" // PXAT sets expiration as Unix timestamp in milliseconds PXAT ExpirationMode = "PXAT" // KEEPTTL keeps the existing TTL KEEPTTL ExpirationMode = "KEEPTTL" ) type ExpirationOption struct { Mode ExpirationMode Value int64 } func (c cmdable) LCS(ctx context.Context, q *LCSQuery) *LCSCmd { cmd := NewLCSCmd(ctx, q) _ = c(ctx, cmd) return cmd } func (c cmdable) MGet(ctx context.Context, keys ...string) *SliceCmd { args := make([]interface{}, 1+len(keys)) args[0] = "mget" for i, key := range keys { args[1+i] = key } cmd := NewSliceCmd(ctx, args...) _ = c(ctx, cmd) return cmd } // MSet is like Set but accepts multiple values: // - MSet("key1", "value1", "key2", "value2") // - MSet([]string{"key1", "value1", "key2", "value2"}) // - MSet(map[string]interface{}{"key1": "value1", "key2": "value2"}) // - MSet(struct), For struct types, see HSet description. func (c cmdable) MSet(ctx context.Context, values ...interface{}) *StatusCmd { args := make([]interface{}, 1, 1+len(values)) args[0] = "mset" args = appendArgs(args, values) cmd := NewStatusCmd(ctx, args...) _ = c(ctx, cmd) return cmd } // MSetNX is like SetNX but accepts multiple values: // - MSetNX("key1", "value1", "key2", "value2") // - MSetNX([]string{"key1", "value1", "key2", "value2"}) // - MSetNX(map[string]interface{}{"key1": "value1", "key2": "value2"}) // - MSetNX(struct), For struct types, see HSet description. func (c cmdable) MSetNX(ctx context.Context, values ...interface{}) *BoolCmd { args := make([]interface{}, 1, 1+len(values)) args[0] = "msetnx" args = appendArgs(args, values) cmd := NewBoolCmd(ctx, args...) _ = c(ctx, cmd) return cmd } type MSetEXArgs struct { Condition SetCondition Expiration *ExpirationOption } // MSetEX sets the given keys to their respective values. // This command is an extension of the MSETNX that adds expiration and XX options. // Available since Redis 8.4 // Important: When this method is used with Cluster clients, all keys // must be in the same hash slot, otherwise CROSSSLOT error will be returned. // For more information, see https://redis.io/commands/msetex func (c cmdable) MSetEX(ctx context.Context, args MSetEXArgs, values ...interface{}) *IntCmd { expandedArgs := appendArgs([]interface{}{}, values) numkeys := len(expandedArgs) / 2 cmdArgs := make([]interface{}, 0, 2+len(expandedArgs)+3) cmdArgs = append(cmdArgs, "msetex", numkeys) cmdArgs = append(cmdArgs, expandedArgs...) if args.Condition != "" { cmdArgs = append(cmdArgs, string(args.Condition)) } if args.Expiration != nil { switch args.Expiration.Mode { case EX: cmdArgs = append(cmdArgs, "ex", args.Expiration.Value) case PX: cmdArgs = append(cmdArgs, "px", args.Expiration.Value) case EXAT: cmdArgs = append(cmdArgs, "exat", args.Expiration.Value) case PXAT: cmdArgs = append(cmdArgs, "pxat", args.Expiration.Value) case KEEPTTL: cmdArgs = append(cmdArgs, "keepttl") } } cmd := NewIntCmd(ctx, cmdArgs...) _ = c(ctx, cmd) return cmd } // Set Redis `SET key value [expiration]` command. // Use expiration for `SETEx`-like behavior. // // Zero expiration means the key has no expiration time. // KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0, // otherwise you will receive an error: (error) ERR syntax error. func (c cmdable) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd { args := make([]interface{}, 3, 5) args[0] = "set" args[1] = key args[2] = value if expiration > 0 { if usePrecise(expiration) { args = append(args, "px", formatMs(ctx, expiration)) } else { args = append(args, "ex", formatSec(ctx, expiration)) } } else if expiration == KeepTTL { args = append(args, "keepttl") } cmd := NewStatusCmd(ctx, args...) _ = c(ctx, cmd) return cmd } // SetArgs provides arguments for the SetArgs function. type SetArgs struct { // Mode can be `NX` or `XX` or empty. Mode string // Zero `TTL` or `Expiration` means that the key has no expiration time. TTL time.Duration ExpireAt time.Time // When Get is true, the command returns the old value stored at key, or nil when key did not exist. Get bool // KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0, // otherwise you will receive an error: (error) ERR syntax error. KeepTTL bool } // SetArgs supports all the options that the SET command supports. // It is the alternative to the Set function when you want // to have more control over the options. func (c cmdable) SetArgs(ctx context.Context, key string, value interface{}, a SetArgs) *StatusCmd { args := []interface{}{"set", key, value} if a.KeepTTL { args = append(args, "keepttl") } if !a.ExpireAt.IsZero() { args = append(args, "exat", a.ExpireAt.Unix()) } if a.TTL > 0 { if usePrecise(a.TTL) { args = append(args, "px", formatMs(ctx, a.TTL)) } else { args = append(args, "ex", formatSec(ctx, a.TTL)) } } if a.Mode != "" { args = append(args, a.Mode) } if a.Get { args = append(args, "get") } cmd := NewStatusCmd(ctx, args...) _ = c(ctx, cmd) return cmd } // SetEx Redis `SETEx key expiration value` command. func (c cmdable) SetEx(ctx context.Context, key string, value interface{}, expiration time.Duration) *StatusCmd { cmd := NewStatusCmd(ctx, "setex", key, formatSec(ctx, expiration), value) _ = c(ctx, cmd) return cmd } // SetNX Redis `SET key value [expiration] NX` command. // // Zero expiration means the key has no expiration time. // KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0, // otherwise you will receive an error: (error) ERR syntax error. func (c cmdable) SetNX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd { var cmd *BoolCmd switch expiration { case 0: // Use old `SETNX` to support old Redis versions. cmd = NewBoolCmd(ctx, "setnx", key, value) case KeepTTL: cmd = NewBoolCmd(ctx, "set", key, value, "keepttl", "nx") default: if usePrecise(expiration) { cmd = NewBoolCmd(ctx, "set", key, value, "px", formatMs(ctx, expiration), "nx") } else { cmd = NewBoolCmd(ctx, "set", key, value, "ex", formatSec(ctx, expiration), "nx") } } _ = c(ctx, cmd) return cmd } // SetXX Redis `SET key value [expiration] XX` command. // // Zero expiration means the key has no expiration time. // KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0, // otherwise you will receive an error: (error) ERR syntax error. func (c cmdable) SetXX(ctx context.Context, key string, value interface{}, expiration time.Duration) *BoolCmd { var cmd *BoolCmd switch expiration { case 0: cmd = NewBoolCmd(ctx, "set", key, value, "xx") case KeepTTL: cmd = NewBoolCmd(ctx, "set", key, value, "keepttl", "xx") default: if usePrecise(expiration) { cmd = NewBoolCmd(ctx, "set", key, value, "px", formatMs(ctx, expiration), "xx") } else { cmd = NewBoolCmd(ctx, "set", key, value, "ex", formatSec(ctx, expiration), "xx") } } _ = c(ctx, cmd) return cmd } func (c cmdable) SetRange(ctx context.Context, key string, offset int64, value string) *IntCmd { cmd := NewIntCmd(ctx, "setrange", key, offset, value) _ = c(ctx, cmd) return cmd } func (c cmdable) StrLen(ctx context.Context, key string) *IntCmd { cmd := NewIntCmd(ctx, "strlen", key) _ = c(ctx, cmd) return cmd }