Return ok boolean instead of OK string in embedded api methods that return ok status. Updated tests to match new return types

This commit is contained in:
Kelvin Clement Mwinuka
2024-05-18 22:49:21 +08:00
parent 7fb236e631
commit c634b1b20e
15 changed files with 1324 additions and 1270 deletions

View File

@@ -59,11 +59,4 @@ func main() {
<-cancelCh <-cancelCh
server.ShutDown() server.ShutDown()
// TODO: For example purposes only! Delete before PR!
// vault, err := echovault.NewEchoVault()
// if err != nil {
// log.Fatal(err)
// }
// newValue, err := vault.HIncrByFloat("key", "field", 7.75)
} }

File diff suppressed because it is too large Load Diff

View File

@@ -19,6 +19,7 @@ import (
"fmt" "fmt"
"github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal"
"github.com/tidwall/resp" "github.com/tidwall/resp"
"strings"
) )
// ACLLoadOptions modifies the behaviour of the ACLLoad function. // ACLLoadOptions modifies the behaviour of the ACLLoad function.
@@ -150,8 +151,8 @@ func (server *EchoVault) ACLUsers() ([]string, error) {
// //
// `user` - User - The user object to add/update. // `user` - User - The user object to add/update.
// //
// Returns: "OK" if the user is successfully created/updated. // Returns: true if the user is successfully created/updated.
func (server *EchoVault) ACLSetUser(user User) (string, error) { func (server *EchoVault) ACLSetUser(user User) (bool, error) {
cmd := []string{"ACL", "SETUSER", user.Username} cmd := []string{"ACL", "SETUSER", user.Username}
if user.Enabled { if user.Enabled {
@@ -238,10 +239,11 @@ func (server *EchoVault) ACLSetUser(user User) (string, error) {
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
if err != nil { if err != nil {
return "", err return false, err
} }
return internal.ParseStringResponse(b) s, err := internal.ParseStringResponse(b)
return strings.EqualFold(s, "ok"), err
} }
// ACLGetUser gets the ACL configuration of the name with the given username. // ACLGetUser gets the ACL configuration of the name with the given username.
@@ -323,14 +325,15 @@ func (server *EchoVault) ACLGetUser(username string) (map[string][]string, error
// //
// `usernames` - ...string - A string of usernames to delete from the ACL module. // `usernames` - ...string - A string of usernames to delete from the ACL module.
// //
// Returns: "OK" if the deletion is successful. // Returns: true if the deletion is successful.
func (server *EchoVault) ACLDelUser(usernames ...string) (string, error) { func (server *EchoVault) ACLDelUser(usernames ...string) (bool, error) {
cmd := append([]string{"ACL", "DELUSER"}, usernames...) cmd := append([]string{"ACL", "DELUSER"}, usernames...)
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
if err != nil { if err != nil {
return "", err return false, err
} }
return internal.ParseStringResponse(b) s, err := internal.ParseStringResponse(b)
return strings.EqualFold(s, "ok"), err
} }
// ACLList lists all the currently loaded ACL users and their rules. // ACLList lists all the currently loaded ACL users and their rules.
@@ -349,8 +352,8 @@ func (server *EchoVault) ACLList() ([]string, error) {
// //
// `options` - ACLLoadOptions - modifies the load behaviour. // `options` - ACLLoadOptions - modifies the load behaviour.
// //
// Returns: "OK" if the load is successful. // Returns: true if the load is successful.
func (server *EchoVault) ACLLoad(options ACLLoadOptions) (string, error) { func (server *EchoVault) ACLLoad(options ACLLoadOptions) (bool, error) {
cmd := []string{"ACL", "LOAD"} cmd := []string{"ACL", "LOAD"}
switch { switch {
case options.Merge: case options.Merge:
@@ -363,19 +366,21 @@ func (server *EchoVault) ACLLoad(options ACLLoadOptions) (string, error) {
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
if err != nil { if err != nil {
return "", err return false, err
} }
return internal.ParseStringResponse(b) s, err := internal.ParseStringResponse(b)
return strings.EqualFold(s, "ok"), err
} }
// ACLSave saves the current ACL configuration to the configured ACL file. // ACLSave saves the current ACL configuration to the configured ACL file.
// //
// Returns: "OK" if the save is successful. // Returns: true if the save is successful.
func (server *EchoVault) ACLSave() (string, error) { func (server *EchoVault) ACLSave() (bool, error) {
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"ACL", "SAVE"}), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"ACL", "SAVE"}), nil, false, true)
if err != nil { if err != nil {
return "", err return false, err
} }
return internal.ParseStringResponse(b) s, err := internal.ParseStringResponse(b)
return strings.EqualFold(s, "ok"), err
} }

View File

@@ -1,6 +1,6 @@
// Copyright 2024 Kelvin Clement Mwinuka // Copyright 2024 Kelvin Clement Mwinuka
// //
// Licensed under the Apache License, Version 2.0 (the "License");s // Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License. // you may not use this file except in compliance with the License.
// You may obtain a copy of the License at // You may obtain a copy of the License at
// //

View File

@@ -78,14 +78,14 @@ type PExpireAtOptions ExpireOptions
// //
// `options` - SetOptions. // `options` - SetOptions.
// //
// Returns: "OK" if the set is successful, If the "Get" flag in SetOptions is set to true, the previous value is returned. // Returns: true if the set is successful, If the "Get" flag in SetOptions is set to true, the previous value is returned.
// //
// Errors: // Errors:
// //
// "key <key> does not exist"" - when the XX flag is set to true and the key does not exist. // "key <key> does not exist"" - when the XX flag is set to true and the key does not exist.
// //
// "key <key> does already exists" - when the NX flag is set to true and the key already exists. // "key <key> does already exists" - when the NX flag is set to true and the key already exists.
func (server *EchoVault) Set(key, value string, options SetOptions) (string, error) { func (server *EchoVault) Set(key, value string, options SetOptions) (string, bool, error) {
cmd := []string{"SET", key, value} cmd := []string{"SET", key, value}
switch { switch {
@@ -112,10 +112,18 @@ func (server *EchoVault) Set(key, value string, options SetOptions) (string, err
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
if err != nil { if err != nil {
return "", err return "", false, err
} }
return internal.ParseStringResponse(b) previousValue, err := internal.ParseStringResponse(b)
if err != nil {
return "", false, err
}
if !options.GET {
previousValue = ""
}
return previousValue, true, nil
} }
// MSet set multiple values at multiple keys with one command. Existing keys are overwritten and non-existent // MSet set multiple values at multiple keys with one command. Existing keys are overwritten and non-existent

View File

@@ -609,7 +609,8 @@ func TestEchoVault_SET(t *testing.T) {
key string key string
value string value string
options SetOptions options SetOptions
want string wantOk bool
wantPrev string
wantErr bool wantErr bool
}{ }{
{ {
@@ -618,7 +619,8 @@ func TestEchoVault_SET(t *testing.T) {
key: "key1", key: "key1",
value: "value1", value: "value1",
options: SetOptions{}, options: SetOptions{},
want: "OK", wantOk: true,
wantPrev: "",
wantErr: false, wantErr: false,
}, },
{ {
@@ -627,7 +629,8 @@ func TestEchoVault_SET(t *testing.T) {
key: "key2", key: "key2",
value: "value2", value: "value2",
options: SetOptions{NX: true}, options: SetOptions{NX: true},
want: "OK", wantOk: true,
wantPrev: "",
wantErr: false, wantErr: false,
}, },
{ {
@@ -638,11 +641,12 @@ func TestEchoVault_SET(t *testing.T) {
ExpireAt: time.Time{}, ExpireAt: time.Time{},
}, },
}, },
key: "key3", key: "key3",
value: "value3", value: "value3",
options: SetOptions{NX: true}, options: SetOptions{NX: true},
want: "", wantOk: false,
wantErr: true, wantPrev: "",
wantErr: true,
}, },
{ {
name: "Set new key value when key exists with XX flag passed", name: "Set new key value when key exists with XX flag passed",
@@ -652,11 +656,12 @@ func TestEchoVault_SET(t *testing.T) {
ExpireAt: time.Time{}, ExpireAt: time.Time{},
}, },
}, },
key: "key4", key: "key4",
value: "value4", value: "value4",
options: SetOptions{XX: true}, options: SetOptions{XX: true},
want: "OK", wantOk: true,
wantErr: false, wantPrev: "",
wantErr: false,
}, },
{ {
name: "Return error when setting non-existent key with XX flag", name: "Return error when setting non-existent key with XX flag",
@@ -664,7 +669,8 @@ func TestEchoVault_SET(t *testing.T) {
key: "key5", key: "key5",
value: "value5", value: "value5",
options: SetOptions{XX: true}, options: SetOptions{XX: true},
want: "", wantOk: false,
wantPrev: "",
wantErr: true, wantErr: true,
}, },
{ {
@@ -673,7 +679,8 @@ func TestEchoVault_SET(t *testing.T) {
key: "key6", key: "key6",
value: "value6", value: "value6",
options: SetOptions{EX: 100}, options: SetOptions{EX: 100},
want: "OK", wantOk: true,
wantPrev: "",
wantErr: false, wantErr: false,
}, },
{ {
@@ -682,7 +689,8 @@ func TestEchoVault_SET(t *testing.T) {
key: "key7", key: "key7",
value: "value7", value: "value7",
options: SetOptions{PX: 4096}, options: SetOptions{PX: 4096},
want: "OK", wantOk: true,
wantPrev: "",
wantErr: false, wantErr: false,
}, },
{ {
@@ -691,7 +699,8 @@ func TestEchoVault_SET(t *testing.T) {
key: "key8", key: "key8",
value: "value8", value: "value8",
options: SetOptions{EXAT: int(mockClock.Now().Add(200 * time.Second).Unix())}, options: SetOptions{EXAT: int(mockClock.Now().Add(200 * time.Second).Unix())},
want: "OK", wantOk: true,
wantPrev: "",
wantErr: false, wantErr: false,
}, },
{ {
@@ -700,7 +709,8 @@ func TestEchoVault_SET(t *testing.T) {
value: "value9", value: "value9",
options: SetOptions{PXAT: int(mockClock.Now().Add(4096 * time.Millisecond).UnixMilli())}, options: SetOptions{PXAT: int(mockClock.Now().Add(4096 * time.Millisecond).UnixMilli())},
presetValues: nil, presetValues: nil,
want: "OK", wantOk: true,
wantPrev: "",
wantErr: false, wantErr: false,
}, },
{ {
@@ -711,11 +721,12 @@ func TestEchoVault_SET(t *testing.T) {
ExpireAt: time.Time{}, ExpireAt: time.Time{},
}, },
}, },
key: "key10", key: "key10",
value: "value10", value: "value10",
options: SetOptions{GET: true, EX: 1000}, options: SetOptions{GET: true, EX: 1000},
want: "previous-value", wantOk: true,
wantErr: false, wantPrev: "previous-value",
wantErr: false,
}, },
{ {
name: "Return nil when GET value is passed and no previous value exists", name: "Return nil when GET value is passed and no previous value exists",
@@ -723,7 +734,8 @@ func TestEchoVault_SET(t *testing.T) {
key: "key11", key: "key11",
value: "value11", value: "value11",
options: SetOptions{GET: true, EX: 1000}, options: SetOptions{GET: true, EX: 1000},
want: "", wantOk: true,
wantPrev: "",
wantErr: false, wantErr: false,
}, },
} }
@@ -734,13 +746,16 @@ func TestEchoVault_SET(t *testing.T) {
presetKeyData(server, context.Background(), k, d) presetKeyData(server, context.Background(), k, d)
} }
} }
got, err := server.Set(tt.key, tt.value, tt.options) previousValue, ok, err := server.Set(tt.key, tt.value, tt.options)
if (err != nil) != tt.wantErr { if (err != nil) != tt.wantErr {
t.Errorf("SET() error = %v, wantErr %v", err, tt.wantErr) t.Errorf("SET() error = %v, wantErr %v", err, tt.wantErr)
return return
} }
if got != tt.want { if ok != tt.wantOk {
t.Errorf("SET() got = %v, want %v", got, tt.want) t.Errorf("SET() ok got = %v, want %v", ok, tt.wantOk)
}
if previousValue != tt.wantPrev {
t.Errorf("SET() previous value got = %v, want %v", previousValue, tt.wantPrev)
} }
}) })
} }

View File

@@ -18,6 +18,7 @@ import (
"fmt" "fmt"
"github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal"
"strconv" "strconv"
"strings"
) )
// LLen returns the length of the list. // LLen returns the length of the list.
@@ -101,29 +102,33 @@ func (server *EchoVault) LIndex(key string, index uint) (string, error) {
// //
// `value` - string - the new value to place at the given index. // `value` - string - the new value to place at the given index.
// //
// Returns: "OK" if the update is successful. // Returns: true if the update is successful.
// //
// Errors: // Errors:
// //
// "LSet command on non-list item" - when the provided key exists but is not a list. // "LSet command on non-list item" - when the provided key exists but is not a list.
// //
// "index must be within list range" - when the index is not within the list boundary. // "index must be within list range" - when the index is not within the list boundary.
func (server *EchoVault) LSet(key string, index int, value string) (string, error) { func (server *EchoVault) LSet(key string, index int, value string) (bool, error) {
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"LSET", key, strconv.Itoa(index), value}), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"LSET", key, strconv.Itoa(index), value}), nil, false, true)
if err != nil { if err != nil {
return "", err return false, err
} }
return internal.ParseStringResponse(b) s, err := internal.ParseStringResponse(b)
return strings.EqualFold(s, "ok"), err
} }
// LTrim work similarly to LRange but instead of returning the new list, it replaces the original list with the // LTrim work similarly to LRange but instead of returning the new list, it replaces the original list with the
// trimmed list. // trimmed list.
func (server *EchoVault) LTrim(key string, start int, end int) (string, error) { //
// Returns: true if the trim is successful.
func (server *EchoVault) LTrim(key string, start int, end int) (bool, error) {
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"LTRIM", key, strconv.Itoa(start), strconv.Itoa(end)}), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"LTRIM", key, strconv.Itoa(start), strconv.Itoa(end)}), nil, false, true)
if err != nil { if err != nil {
return "", err return false, err
} }
return internal.ParseStringResponse(b) s, err := internal.ParseStringResponse(b)
return strings.EqualFold(s, "ok"), err
} }
// LRem removes 'count' instances of the specified element from the list. // LRem removes 'count' instances of the specified element from the list.
@@ -136,17 +141,23 @@ func (server *EchoVault) LTrim(key string, start int, end int) (string, error) {
// //
// `value` - string - the element to remove. // `value` - string - the element to remove.
// //
// Returns: "OK" if the removal was successful. // Returns: true if the removal was successful.
// //
// Errors: // Errors:
// //
// "LRem command on non-list item" - when the provided key exists but is not a list. // "LRem command on non-list item" - when the provided key exists but is not a list.
func (server *EchoVault) LRem(key string, count int, value string) (string, error) { func (server *EchoVault) LRem(key string, count int, value string) (bool, error) {
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"LREM", key, strconv.Itoa(count), value}), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{
"LREM", key, strconv.Itoa(count), value}),
nil,
false,
true,
)
if err != nil { if err != nil {
return "", err return false, err
} }
return internal.ParseStringResponse(b) s, err := internal.ParseStringResponse(b)
return strings.EqualFold(s, "ok"), err
} }
// LMove moves an element from one list to another. // LMove moves an element from one list to another.
@@ -163,19 +174,20 @@ func (server *EchoVault) LRem(key string, count int, value string) (string, erro
// `whereTo` - string - either "LEFT" or "RIGHT". If "LEFT", the element is added to the beginning of the destination list. // `whereTo` - string - either "LEFT" or "RIGHT". If "LEFT", the element is added to the beginning of the destination list.
// If "RIGHT", the element is added to the end of the destination list. // If "RIGHT", the element is added to the end of the destination list.
// //
// Returns: "OK" if the removal was successful. // Returns: true if the removal was successful.
// //
// Errors: // Errors:
// //
// "both source and destination must be lists" - when either source or destination are not lists. // "both source and destination must be lists" - when either source or destination are not lists.
// //
// "wherefrom and whereto arguments must be either LEFT or RIGHT" - if whereFrom or whereTo are not either "LEFT" or "RIGHT". // "wherefrom and whereto arguments must be either LEFT or RIGHT" - if whereFrom or whereTo are not either "LEFT" or "RIGHT".
func (server *EchoVault) LMove(source, destination, whereFrom, whereTo string) (string, error) { func (server *EchoVault) LMove(source, destination, whereFrom, whereTo string) (bool, error) {
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"LMOVE", source, destination, whereFrom, whereTo}), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"LMOVE", source, destination, whereFrom, whereTo}), nil, false, true)
if err != nil { if err != nil {
return "", err return false, err
} }
return internal.ParseStringResponse(b) s, err := internal.ParseStringResponse(b)
return strings.EqualFold(s, "ok"), err
} }
// LPop pops an element from the start of the list and return it. // LPop pops an element from the start of the list and return it.

View File

@@ -176,7 +176,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination string destination string
whereFrom string whereFrom string
whereTo string whereTo string
want string want bool
wantErr bool wantErr bool
}{ }{
{ {
@@ -190,7 +190,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination: "destination1", destination: "destination1",
whereFrom: "LEFT", whereFrom: "LEFT",
whereTo: "LEFT", whereTo: "LEFT",
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -204,7 +204,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination: "destination2", destination: "destination2",
whereFrom: "LEFT", whereFrom: "LEFT",
whereTo: "RIGHT", whereTo: "RIGHT",
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -218,7 +218,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination: "destination3", destination: "destination3",
whereFrom: "RIGHT", whereFrom: "RIGHT",
whereTo: "LEFT", whereTo: "LEFT",
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -232,7 +232,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination: "destination4", destination: "destination4",
whereFrom: "RIGHT", whereFrom: "RIGHT",
whereTo: "RIGHT", whereTo: "RIGHT",
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -245,7 +245,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination: "destination5", destination: "destination5",
whereFrom: "LEFT", whereFrom: "LEFT",
whereTo: "LEFT", whereTo: "LEFT",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -259,7 +259,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination: "destination6", destination: "destination6",
whereFrom: "LEFT", whereFrom: "LEFT",
whereTo: "LEFT", whereTo: "LEFT",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -272,7 +272,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination: "destination7", destination: "destination7",
whereFrom: "LEFT", whereFrom: "LEFT",
whereTo: "LEFT", whereTo: "LEFT",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -286,7 +286,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination: "destination8", destination: "destination8",
whereFrom: "LEFT", whereFrom: "LEFT",
whereTo: "LEFT", whereTo: "LEFT",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -297,7 +297,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination: "destination9", destination: "destination9",
whereFrom: "LEFT", whereFrom: "LEFT",
whereTo: "LEFT", whereTo: "LEFT",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -308,7 +308,7 @@ func TestEchoVault_LMOVE(t *testing.T) {
destination: "destination10", destination: "destination10",
whereFrom: "LEFT", whereFrom: "LEFT",
whereTo: "LEFT", whereTo: "LEFT",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
} }
@@ -664,7 +664,7 @@ func TestEchoVault_LREM(t *testing.T) {
key string key string
count int count int
value string value string
want string want bool
wantErr bool wantErr bool
}{ }{
{ {
@@ -674,7 +674,7 @@ func TestEchoVault_LREM(t *testing.T) {
key: "key1", key: "key1",
count: 3, count: 3,
value: "4", value: "4",
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -684,7 +684,7 @@ func TestEchoVault_LREM(t *testing.T) {
key: "key2", key: "key2",
count: -3, count: -3,
value: "4", value: "4",
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -694,7 +694,7 @@ func TestEchoVault_LREM(t *testing.T) {
key: "LremKey8", key: "LremKey8",
count: 0, count: 0,
value: "value1", value: "value1",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
} }
@@ -729,7 +729,7 @@ func TestEchoVault_LSET(t *testing.T) {
key string key string
index int index int
value string value string
want string want bool
wantErr bool wantErr bool
}{ }{
{ {
@@ -739,7 +739,7 @@ func TestEchoVault_LSET(t *testing.T) {
key: "key1", key: "key1",
index: 3, index: 3,
value: "new-value", value: "new-value",
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -749,7 +749,7 @@ func TestEchoVault_LSET(t *testing.T) {
key: "key2", key: "key2",
index: 0, index: 0,
value: "new-value", value: "new-value",
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -759,7 +759,7 @@ func TestEchoVault_LSET(t *testing.T) {
key: "key3", key: "key3",
index: 1, index: 1,
value: "new-value", value: "new-value",
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -769,7 +769,7 @@ func TestEchoVault_LSET(t *testing.T) {
key: "key4", key: "key4",
index: 0, index: 0,
value: "element", value: "element",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -779,7 +779,7 @@ func TestEchoVault_LSET(t *testing.T) {
key: "key5", key: "key5",
index: 0, index: 0,
value: "element", value: "element",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -789,7 +789,7 @@ func TestEchoVault_LSET(t *testing.T) {
key: "key6", key: "key6",
index: 3, index: 3,
value: "element", value: "element",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -799,7 +799,7 @@ func TestEchoVault_LSET(t *testing.T) {
key: "key7", key: "key7",
index: -1, index: -1,
value: "element", value: "element",
want: "", want: false,
wantErr: true, wantErr: true,
}, },
} }
@@ -834,7 +834,7 @@ func TestEchoVault_LTRIM(t *testing.T) {
key string key string
start int start int
end int end int
want string want bool
wantErr bool wantErr bool
}{ }{
{ {
@@ -847,7 +847,7 @@ func TestEchoVault_LTRIM(t *testing.T) {
key: "key1", key: "key1",
start: 3, start: 3,
end: 6, end: 6,
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -857,7 +857,7 @@ func TestEchoVault_LTRIM(t *testing.T) {
key: "key2", key: "key2",
start: 5, start: 5,
end: -1, end: -1,
want: "OK", want: true,
wantErr: false, wantErr: false,
}, },
{ {
@@ -867,7 +867,7 @@ func TestEchoVault_LTRIM(t *testing.T) {
key: "key3", key: "key3",
start: 3, start: 3,
end: 1, end: 1,
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -877,7 +877,7 @@ func TestEchoVault_LTRIM(t *testing.T) {
key: "key4", key: "key4",
start: 0, start: 0,
end: 2, end: 2,
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -887,7 +887,7 @@ func TestEchoVault_LTRIM(t *testing.T) {
key: "key5", key: "key5",
start: 0, start: 0,
end: 3, end: 3,
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -897,7 +897,7 @@ func TestEchoVault_LTRIM(t *testing.T) {
key: "key6", key: "key6",
start: -1, start: -1,
end: 3, end: 3,
want: "", want: false,
wantErr: true, wantErr: true,
}, },
{ {
@@ -907,7 +907,7 @@ func TestEchoVault_LTRIM(t *testing.T) {
key: "key7", key: "key7",
start: 10, start: 10,
end: 11, end: 11,
want: "", want: false,
wantErr: true, wantErr: true,
}, },
} }

View File

@@ -19,6 +19,7 @@ import (
"github.com/echovault/echovault/internal" "github.com/echovault/echovault/internal"
"github.com/tidwall/resp" "github.com/tidwall/resp"
"net" "net"
"strings"
) )
type conn struct { type conn struct {
@@ -176,14 +177,15 @@ func (server *EchoVault) PUnsubscribe(tag string, patterns ...string) {
// //
// `message` - string - The message to publish to the specified channel. // `message` - string - The message to publish to the specified channel.
// //
// Returns: "OK" when the publish is successful. This does not indicate whether each subscriber has received the message, // Returns: true when the publish is successful. This does not indicate whether each subscriber has received the message,
// only that the message has been published. // only that the message has been published.
func (server *EchoVault) Publish(channel, message string) (string, error) { func (server *EchoVault) Publish(channel, message string) (bool, error) {
b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"PUBLISH", channel, message}), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand([]string{"PUBLISH", channel, message}), nil, false, true)
if err != nil { if err != nil {
return "", err return false, err
} }
return internal.ParseStringResponse(b) s, err := internal.ParseStringResponse(b)
return strings.EqualFold(s, "ok"), err
} }
// PubSubChannels returns the list of channels & patterns that match the glob pattern provided. // PubSubChannels returns the list of channels & patterns that match the glob pattern provided.

View File

@@ -86,6 +86,8 @@ func (server *EchoVault) SDiff(keys ...string) ([]string, error) {
// SDiffStore works like SDiff but instead of returning the resulting set elements, the resulting set is stored // SDiffStore works like SDiff but instead of returning the resulting set elements, the resulting set is stored
// at the 'destination' key. // at the 'destination' key.
//
// Returns: an integer representing the cardinality of the new set.
func (server *EchoVault) SDiffStore(destination string, keys ...string) (int, error) { func (server *EchoVault) SDiffStore(destination string, keys ...string) (int, error) {
cmd := append([]string{"SDIFFSTORE", destination}, keys...) cmd := append([]string{"SDIFFSTORE", destination}, keys...)
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)
@@ -334,6 +336,8 @@ func (server *EchoVault) SUnion(keys ...string) ([]string, error) {
// SUnionStore store works like SUnion but instead of returning the resulting elements, it stores the resulting // SUnionStore store works like SUnion but instead of returning the resulting elements, it stores the resulting
// set at the 'destination' key. The return value is an integer representing the cardinality of the new set. // set at the 'destination' key. The return value is an integer representing the cardinality of the new set.
//
// Returns: an integer representing the cardinality of the new union set.
func (server *EchoVault) SUnionStore(destination string, keys ...string) (int, error) { func (server *EchoVault) SUnionStore(destination string, keys ...string) (int, error) {
cmd := append([]string{"SUNIONSTORE", destination}, keys...) cmd := append([]string{"SUNIONSTORE", destination}, keys...)
b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true) b, err := server.handleCommand(server.context, internal.EncodeCommand(cmd), nil, false, true)

View File

@@ -490,11 +490,13 @@ func handleSave(params internal.HandlerFuncParams) ([]byte, error) {
func Commands() []internal.Command { func Commands() []internal.Command {
return []internal.Command{ return []internal.Command{
{ {
Command: "auth", Command: "auth",
Module: constants.ACLModule, Module: constants.ACLModule,
Categories: []string{constants.ConnectionCategory, constants.SlowCategory}, Categories: []string{constants.ConnectionCategory, constants.SlowCategory},
Description: "(AUTH [username] password) Authenticates the connection", Description: `(AUTH [username] password)
Sync: false, Authenticates the connection. If the username is not provided, the connection will be authenticated against the
default ACL user. Otherwise, it is authenticated against the ACL user with the provided username.`,
Sync: false,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
Channels: make([]string, 0), Channels: make([]string, 0),
@@ -522,8 +524,8 @@ func Commands() []internal.Command {
Command: "cat", Command: "cat",
Module: constants.ACLModule, Module: constants.ACLModule,
Categories: []string{constants.SlowCategory}, Categories: []string{constants.SlowCategory},
Description: `(ACL CAT [category]) List all the categories. Description: `(ACL CAT [category]) Lists all the categories.
If the optional category is provided, list all the commands in the category`, If the optional category is provided, lists all the commands in the category.`,
Sync: false, Sync: false,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
@@ -538,7 +540,7 @@ If the optional category is provided, list all the commands in the category`,
Command: "users", Command: "users",
Module: constants.ACLModule, Module: constants.ACLModule,
Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory},
Description: "(ACL USERS) List all usernames of the configured ACL users", Description: "(ACL USERS) Lists all usernames of the configured ACL users.",
Sync: false, Sync: false,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
@@ -568,7 +570,7 @@ If the optional category is provided, list all the commands in the category`,
Command: "getuser", Command: "getuser",
Module: constants.ACLModule, Module: constants.ACLModule,
Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory},
Description: "(ACL GETUSER username) List the ACL rules of a user", Description: "(ACL GETUSER username) List the ACL rules of a user.",
Sync: false, Sync: false,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
@@ -580,11 +582,12 @@ If the optional category is provided, list all the commands in the category`,
HandlerFunc: handleGetUser, HandlerFunc: handleGetUser,
}, },
{ {
Command: "deluser", Command: "deluser",
Module: constants.ACLModule, Module: constants.ACLModule,
Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory},
Description: "(ACL DELUSER username [username ...]) Deletes users and terminates their connections. Cannot delete default user", Description: `(ACL DELUSER username [username ...])
Sync: true, Deletes users and terminates their connections. Cannot delete default user.`,
Sync: true,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
Channels: make([]string, 0), Channels: make([]string, 0),
@@ -598,7 +601,7 @@ If the optional category is provided, list all the commands in the category`,
Command: "whoami", Command: "whoami",
Module: constants.ACLModule, Module: constants.ACLModule,
Categories: []string{constants.FastCategory}, Categories: []string{constants.FastCategory},
Description: "(ACL WHOAMI) Returns the authenticated user of the current connection", Description: "(ACL WHOAMI) Returns the authenticated user of the current connection.",
Sync: true, Sync: true,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
@@ -613,7 +616,7 @@ If the optional category is provided, list all the commands in the category`,
Command: "list", Command: "list",
Module: constants.ACLModule, Module: constants.ACLModule,
Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory},
Description: "(ACL LIST) Dumps effective acl rules in acl config file format", Description: "(ACL LIST) Dumps effective acl rules in ACL DSL format.",
Sync: true, Sync: true,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
@@ -646,7 +649,7 @@ When 'REPLACE' is passed, users from config file who share a username with users
Command: "save", Command: "save",
Module: constants.ACLModule, Module: constants.ACLModule,
Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory},
Description: "(ACL SAVE) Saves the effective ACL rules the configured ACL config file", Description: "(ACL SAVE) Saves the effective ACL rules the configured ACL config file.",
Sync: true, Sync: true,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{

View File

@@ -195,7 +195,7 @@ func Commands() []internal.Command {
Command: "commands", Command: "commands",
Module: constants.AdminModule, Module: constants.AdminModule,
Categories: []string{constants.AdminCategory, constants.SlowCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory},
Description: "Get a list of all the commands in available on the echovault with categories and descriptions", Description: "Get a list of all the commands in available on the echovault with categories and descriptions.",
Sync: false, Sync: false,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
@@ -232,8 +232,8 @@ func Commands() []internal.Command {
{ {
Command: "count", Command: "count",
Module: constants.AdminModule, Module: constants.AdminModule,
Categories: []string{constants.SlowCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory},
Description: "Get the dumber of commands in the echovault", Description: "Get the dumber of commands in the echovault instance.",
Sync: false, Sync: false,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
@@ -245,9 +245,9 @@ func Commands() []internal.Command {
{ {
Command: "list", Command: "list",
Module: constants.AdminModule, Module: constants.AdminModule,
Categories: []string{constants.SlowCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory},
Description: `(COMMAND LIST [FILTERBY <ACLCAT category | PATTERN pattern | MODULE module>]) Get the list of command names. Description: `(COMMAND LIST [FILTERBY <ACLCAT category | PATTERN pattern | MODULE module>])
Allows for filtering by ACL category or glob pattern.`, Get the list of command names. Allows for filtering by ACL category or glob pattern.`,
Sync: false, Sync: false,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
@@ -262,7 +262,7 @@ Allows for filtering by ACL category or glob pattern.`,
Command: "save", Command: "save",
Module: constants.AdminModule, Module: constants.AdminModule,
Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory},
Description: "(SAVE) Trigger a snapshot save", Description: "(SAVE) Trigger a snapshot save.",
Sync: true, Sync: true,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
@@ -299,7 +299,7 @@ Allows for filtering by ACL category or glob pattern.`,
Command: "rewriteaof", Command: "rewriteaof",
Module: constants.AdminModule, Module: constants.AdminModule,
Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory},
Description: "(REWRITEAOF) Trigger re-writing of append process", Description: "(REWRITEAOF) Trigger re-writing of append process.",
Sync: false, Sync: false,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
@@ -352,11 +352,12 @@ module's key extraction and handler functions.`,
}, },
}, },
{ {
Command: "unload", Command: "unload",
Module: constants.AdminModule, Module: constants.AdminModule,
Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory}, Categories: []string{constants.AdminCategory, constants.SlowCategory, constants.DangerousCategory},
Description: `(MODULE UNLOAD name) Unloads a module based on the its name as displayed by the MODULE LIST command.`, Description: `(MODULE UNLOAD name)
Sync: true, Unloads a module based on the its name as displayed by the MODULE LIST command.`,
Sync: true,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
Channels: make([]string, 0), ReadKeys: make([]string, 0), WriteKeys: make([]string, 0), Channels: make([]string, 0), ReadKeys: make([]string, 0), WriteKeys: make([]string, 0),

View File

@@ -35,11 +35,13 @@ func handlePing(params internal.HandlerFuncParams) ([]byte, error) {
func Commands() []internal.Command { func Commands() []internal.Command {
return []internal.Command{ return []internal.Command{
{ {
Command: "ping", Command: "ping",
Module: constants.ConnectionModule, Module: constants.ConnectionModule,
Categories: []string{constants.FastCategory, constants.ConnectionCategory}, Categories: []string{constants.ConnectionCategory, constants.FastCategory},
Description: "(PING [value]) Ping the echovault. If a value is provided, the value will be echoed.", Description: `(PING [message])
Sync: false, Ping the echovault server. If a message is provided, the message will be echoed back to the client.
Otherwise, the server will return "PONG".`,
Sync: false,
KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) { KeyExtractionFunc: func(cmd []string) (internal.KeyExtractionFuncResult, error) {
return internal.KeyExtractionFuncResult{ return internal.KeyExtractionFuncResult{
Channels: make([]string, 0), Channels: make([]string, 0),

View File

@@ -511,19 +511,21 @@ func handlePop(params internal.HandlerFuncParams) ([]byte, error) {
func Commands() []internal.Command { func Commands() []internal.Command {
return []internal.Command{ return []internal.Command{
{ {
Command: "lpush", Command: "lpush",
Module: constants.ListModule, Module: constants.ListModule,
Categories: []string{constants.ListCategory, constants.WriteCategory, constants.FastCategory}, Categories: []string{constants.ListCategory, constants.WriteCategory, constants.FastCategory},
Description: "(LPUSH key element [element ...]) Prepends one or more values to the beginning of a list, creates the list if it does not exist.", Description: `(LPUSH key element [element ...])
Prepends one or more values to the beginning of a list, creates the list if it does not exist.`,
Sync: true, Sync: true,
KeyExtractionFunc: lpushKeyFunc, KeyExtractionFunc: lpushKeyFunc,
HandlerFunc: handleLPush, HandlerFunc: handleLPush,
}, },
{ {
Command: "lpushx", Command: "lpushx",
Module: constants.ListModule, Module: constants.ListModule,
Categories: []string{constants.ListCategory, constants.WriteCategory, constants.FastCategory}, Categories: []string{constants.ListCategory, constants.WriteCategory, constants.FastCategory},
Description: "(LPUSHX key element [element ...]) Prepends a value to the beginning of a list only if the list exists.", Description: `(LPUSHX key element [element ...])
Prepends a value to the beginning of a list only if the list exists.`,
Sync: true, Sync: true,
KeyExtractionFunc: lpushKeyFunc, KeyExtractionFunc: lpushKeyFunc,
HandlerFunc: handleLPush, HandlerFunc: handleLPush,
@@ -558,7 +560,7 @@ func Commands() []internal.Command {
{ {
Command: "lindex", Command: "lindex",
Module: constants.ListModule, Module: constants.ListModule,
Categories: []string{constants.ListCategory, constants.ReadCategory, constants.SlowCategory}, Categories: []string{constants.ListCategory, constants.ReadCategory, constants.FastCategory},
Description: "(LINDEX key index) Gets list element by index.", Description: "(LINDEX key index) Gets list element by index.",
Sync: false, Sync: false,
KeyExtractionFunc: lindexKeyFunc, KeyExtractionFunc: lindexKeyFunc,
@@ -567,7 +569,7 @@ func Commands() []internal.Command {
{ {
Command: "lset", Command: "lset",
Module: constants.ListModule, Module: constants.ListModule,
Categories: []string{constants.ListCategory, constants.WriteCategory, constants.SlowCategory}, Categories: []string{constants.ListCategory, constants.WriteCategory, constants.FastCategory},
Description: "(LSET key index element) Sets the value of an element in a list by its index.", Description: "(LSET key index element) Sets the value of an element in a list by its index.",
Sync: true, Sync: true,
KeyExtractionFunc: lsetKeyFunc, KeyExtractionFunc: lsetKeyFunc,
@@ -586,16 +588,17 @@ func Commands() []internal.Command {
Command: "lrem", Command: "lrem",
Module: constants.ListModule, Module: constants.ListModule,
Categories: []string{constants.ListCategory, constants.WriteCategory, constants.SlowCategory}, Categories: []string{constants.ListCategory, constants.WriteCategory, constants.SlowCategory},
Description: "(LREM key count element) Remove elements from list.", Description: "(LREM key count element) Remove <count> elements from list.",
Sync: true, Sync: true,
KeyExtractionFunc: lremKeyFunc, KeyExtractionFunc: lremKeyFunc,
HandlerFunc: handleLRem, HandlerFunc: handleLRem,
}, },
{ {
Command: "lmove", Command: "lmove",
Module: constants.ListModule, Module: constants.ListModule,
Categories: []string{constants.ListCategory, constants.WriteCategory, constants.SlowCategory}, Categories: []string{constants.ListCategory, constants.WriteCategory, constants.SlowCategory},
Description: "(LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>) Move element from one list to the other specifying left/right for both lists.", Description: `(LMOVE source destination <LEFT | RIGHT> <LEFT | RIGHT>)
Move element from one list to the other specifying left/right for both lists.`,
Sync: true, Sync: true,
KeyExtractionFunc: lmoveKeyFunc, KeyExtractionFunc: lmoveKeyFunc,
HandlerFunc: handleLMove, HandlerFunc: handleLMove,

View File

@@ -793,10 +793,11 @@ func handleSUNIONSTORE(params internal.HandlerFuncParams) ([]byte, error) {
func Commands() []internal.Command { func Commands() []internal.Command {
return []internal.Command{ return []internal.Command{
{ {
Command: "sadd", Command: "sadd",
Module: constants.SetModule, Module: constants.SetModule,
Categories: []string{constants.SetCategory, constants.WriteCategory, constants.FastCategory}, Categories: []string{constants.SetCategory, constants.WriteCategory, constants.FastCategory},
Description: "(SADD key member [member...]) Add one or more members to the set. If the set does not exist, it's created.", Description: `(SADD key member [member...])
Add one or more members to the set. If the set does not exist, it's created.`,
Sync: true, Sync: true,
KeyExtractionFunc: saddKeyFunc, KeyExtractionFunc: saddKeyFunc,
HandlerFunc: handleSADD, HandlerFunc: handleSADD,
@@ -826,7 +827,7 @@ All keys that are non-existed or hold values that are not sets will be skipped.`
Module: constants.SetModule, Module: constants.SetModule,
Categories: []string{constants.SetCategory, constants.WriteCategory, constants.SlowCategory}, Categories: []string{constants.SetCategory, constants.WriteCategory, constants.SlowCategory},
Description: `(SDIFFSTORE destination key [key...]) Works the same as SDIFF but also stores the result at 'destination'. Description: `(SDIFFSTORE destination key [key...]) Works the same as SDIFF but also stores the result at 'destination'.
Returns the cardinality of the new set`, Returns the cardinality of the new set.`,
Sync: true, Sync: true,
KeyExtractionFunc: sdiffstoreKeyFunc, KeyExtractionFunc: sdiffstoreKeyFunc,
HandlerFunc: handleSDIFFSTORE, HandlerFunc: handleSDIFFSTORE,
@@ -841,10 +842,11 @@ Returns the cardinality of the new set`,
HandlerFunc: handleSINTER, HandlerFunc: handleSINTER,
}, },
{ {
Command: "sintercard", Command: "sintercard",
Module: constants.SetModule, Module: constants.SetModule,
Categories: []string{constants.SetCategory, constants.ReadCategory, constants.SlowCategory}, Categories: []string{constants.SetCategory, constants.ReadCategory, constants.SlowCategory},
Description: "(SINTERCARD key [key...] [LIMIT limit]) Returns the cardinality of the intersection between multiple sets.", Description: `(SINTERCARD key [key...] [LIMIT limit])
Returns the cardinality of the intersection between multiple sets.`,
Sync: false, Sync: false,
KeyExtractionFunc: sintercardKeyFunc, KeyExtractionFunc: sintercardKeyFunc,
HandlerFunc: handleSINTERCARD, HandlerFunc: handleSINTERCARD,