Skip to content

Commit

Permalink
Go: Implementing List commands (valkey-io#2370)
Browse files Browse the repository at this point in the history
* Go: Implementing List commands

Signed-off-by: Janhavi Gupta <[email protected]>
  • Loading branch information
janhavigupta007 authored Oct 3, 2024
1 parent 45c1f15 commit 1496c84
Show file tree
Hide file tree
Showing 5 changed files with 609 additions and 5 deletions.
86 changes: 85 additions & 1 deletion go/api/base_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
type BaseClient interface {
StringCommands
HashCommands
ListCommands

// Close terminates the client by closing all associated resources.
Close()
Expand Down Expand Up @@ -135,7 +136,10 @@ func toCStrings(args []string) ([]C.uintptr_t, []C.ulong) {
stringLengths := make([]C.ulong, len(args))
for i, str := range args {
bytes := utils.StringToBytes(str)
ptr := uintptr(unsafe.Pointer(&bytes[0]))
var ptr uintptr
if len(str) > 0 {
ptr = uintptr(unsafe.Pointer(&bytes[0]))
}
cStrings[i] = C.uintptr_t(ptr)
stringLengths[i] = C.size_t(len(str))
}
Expand Down Expand Up @@ -428,3 +432,83 @@ func (client *baseClient) HStrLen(key string, field string) (Result[int64], erro

return handleLongResponse(result)
}

func (client *baseClient) LPush(key string, elements []string) (Result[int64], error) {
result, err := client.executeCommand(C.LPush, append([]string{key}, elements...))
if err != nil {
return CreateNilInt64Result(), err
}

return handleLongResponse(result)
}

func (client *baseClient) LPop(key string) (Result[string], error) {
result, err := client.executeCommand(C.LPop, []string{key})
if err != nil {
return CreateNilStringResult(), err
}

return handleStringOrNullResponse(result)
}

func (client *baseClient) LPopCount(key string, count int64) ([]Result[string], error) {
result, err := client.executeCommand(C.LPop, []string{key, utils.IntToString(count)})
if err != nil {
return nil, err
}

return handleStringArrayOrNullResponse(result)
}

func (client *baseClient) LPos(key string, element string) (Result[int64], error) {
result, err := client.executeCommand(C.LPos, []string{key, element})
if err != nil {
return CreateNilInt64Result(), err
}

return handleLongOrNullResponse(result)
}

func (client *baseClient) LPosWithOptions(key string, element string, options *LPosOptions) (Result[int64], error) {
result, err := client.executeCommand(C.LPos, append([]string{key, element}, options.toArgs()...))
if err != nil {
return CreateNilInt64Result(), err
}

return handleLongOrNullResponse(result)
}

func (client *baseClient) LPosCount(key string, element string, count int64) ([]Result[int64], error) {
result, err := client.executeCommand(C.LPos, []string{key, element, CountKeyword, utils.IntToString(count)})
if err != nil {
return nil, err
}

return handleLongArrayResponse(result)
}

func (client *baseClient) LPosCountWithOptions(
key string,
element string,
count int64,
options *LPosOptions,
) ([]Result[int64], error) {
result, err := client.executeCommand(
C.LPos,
append([]string{key, element, CountKeyword, utils.IntToString(count)}, options.toArgs()...),
)
if err != nil {
return nil, err
}

return handleLongArrayResponse(result)
}

func (client *baseClient) RPush(key string, elements []string) (Result[int64], error) {
result, err := client.executeCommand(C.RPush, append([]string{key}, elements...))
if err != nil {
return CreateNilInt64Result(), err
}

return handleLongResponse(result)
}
58 changes: 57 additions & 1 deletion go/api/command_options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

package api

import "strconv"
import (
"strconv"

"github.com/valkey-io/valkey-glide/go/glide/utils"
)

// SetOptions represents optional arguments for the [api.StringCommands.SetWithOptions] command.
//
Expand Down Expand Up @@ -147,3 +151,55 @@ const (
UnixMilliseconds ExpiryType = "PXAT" // expire the value after the Unix time specified by [api.Expiry.Count], in milliseconds
Persist ExpiryType = "PERSIST" // Remove the expiry associated with the key
)

// LPosOptions represents optional arguments for the [api.ListCommands.LPosWithOptions] and
// [api.ListCommands.LPosCountWithOptions] commands.
//
// See [valkey.io]
//
// [valkey.io]: https://valkey.io/commands/lpos/
type LPosOptions struct {
// Represents if the rank option is set.
IsRankSet bool
// The rank of the match to return.
Rank int64
// Represents if the max length parameter is set.
IsMaxLenSet bool
// The maximum number of comparisons to make between the element and the items in the list.
MaxLen int64
}

func NewLPosOptionsBuilder() *LPosOptions {
return &LPosOptions{}
}

func (lposOptions *LPosOptions) SetRank(rank int64) *LPosOptions {
lposOptions.IsRankSet = true
lposOptions.Rank = rank
return lposOptions
}

func (lposOptions *LPosOptions) SetMaxLen(maxLen int64) *LPosOptions {
lposOptions.IsMaxLenSet = true
lposOptions.MaxLen = maxLen
return lposOptions
}

func (opts *LPosOptions) toArgs() []string {
args := []string{}
if opts.IsRankSet {
args = append(args, RankKeyword, utils.IntToString(opts.Rank))
}

if opts.IsMaxLenSet {
args = append(args, MaxLenKeyword, utils.IntToString(opts.MaxLen))
}

return args
}

const (
CountKeyword string = "COUNT" // Valkey API keyword used to extract specific number of matching indices from a list.
RankKeyword string = "RANK" // Valkey API keyword use to determine the rank of the match to return.
MaxLenKeyword string = "MAXLEN" // Valkey API keyword used to determine the maximum number of list items to compare.
)
195 changes: 195 additions & 0 deletions go/api/list_commands.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// Copyright Valkey GLIDE Project Contributors - SPDX Identifier: Apache-2.0

package api

// Supports commands and transactions for the "List Commands" group for standalone and cluster clients.
//
// See [valkey.io] for details.
//
// [valkey.io]: https://valkey.io/commands/?group=list
type ListCommands interface {
// Inserts all the specified values at the head of the list stored at key. elements are inserted one after the other to the
// head of the list, from the leftmost element to the rightmost element. If key does not exist, it is created as an empty
// list before performing the push operation.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The key of the list.
// elements - The elements to insert at the head of the list stored at key.
//
// Return value:
// A api.Result[int64] containing the length of the list after the push operation.
//
// For example:
// result, err := client.LPush("my_list", []string{"value1", "value2"})
// result.Value(): 2
// result.IsNil(): false
//
// [valkey.io]: https://valkey.io/commands/lpush/
LPush(key string, elements []string) (Result[int64], error)

// Removes and returns the first elements of the list stored at key. The command pops a single element from the beginning
// of the list.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The key of the list.
//
// Return value:
// The Result[string] containing the value of the first element.
// If key does not exist, [api.CreateNilStringResult()] will be returned.
//
// For example:
// 1. result, err := client.LPush("my_list", []string{"value1", "value2"})
// value, err := client.LPop("my_list")
// value.Value(): "value2"
// result.IsNil(): false
// 2. result, err := client.LPop("non_existent")
// result.Value(): ""
// result.IsNil(); true
//
// [valkey.io]: https://valkey.io/commands/lpop/
LPop(key string) (Result[string], error)

// Removes and returns up to count elements of the list stored at key, depending on the list's length.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The key of the list.
// count - The count of the elements to pop from the list.
//
// Return value:
// An array of the popped elements as Result[string] will be returned depending on the list's length
// If key does not exist, nil will be returned.
//
// For example:
// 1. result, err := client.LPopCount("my_list", 2)
// result: []api.Result[string]{api.CreateStringResult("value1"), api.CreateStringResult("value2")}
// 2. result, err := client.LPopCount("non_existent")
// result: nil
//
// [valkey.io]: https://valkey.io/commands/lpop/
LPopCount(key string, count int64) ([]Result[string], error)

// Returns the index of the first occurrence of element inside the list specified by key. If no match is found,
// [api.CreateNilInt64Result()] is returned.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The name of the list.
// element - The value to search for within the list.
//
// Return value:
// The Result[int64] containing the index of the first occurrence of element, or [api.CreateNilInt64Result()] if element is
// not in the list.
//
// For example:
// result, err := client.RPush("my_list", []string{"a", "b", "c", "d", "e", "e"})
// position, err := client.LPos("my_list", "e")
// position.Value(): 4
// position.IsNil(): false
//
// [valkey.io]: https://valkey.io/commands/lpos/
LPos(key string, element string) (Result[int64], error)

// Returns the index of an occurrence of element within a list based on the given options. If no match is found,
// [api.CreateNilInt64Result()] is returned.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The name of the list.
// element - The value to search for within the list.
// options - The LPos options.
//
// Return value:
// The Result[int64] containing the index of element, or [api.CreateNilInt64Result()] if element is not in the list.
//
// For example:
// 1. result, err := client.RPush("my_list", []string{"a", "b", "c", "d", "e", "e"})
// result, err := client.LPosWithOptions("my_list", "e", api.NewLPosOptionsBuilder().SetRank(2))
// result.Value(): 5 (Returns the second occurrence of the element "e")
// 2. result, err := client.RPush("my_list", []string{"a", "b", "c", "d", "e", "e"})
// result, err := client.LPosWithOptions("my_list", "e", api.NewLPosOptionsBuilder().SetRank(1).SetMaxLen(1000))
// result.Value(): 4
//
// [valkey.io]: https://valkey.io/commands/lpos/
LPosWithOptions(key string, element string, options *LPosOptions) (Result[int64], error)

// Returns an array of indices of matching elements within a list.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The name of the list.
// element - The value to search for within the list.
// count - The number of matches wanted.
//
// Return value:
// An array that holds the indices of the matching elements within the list.
//
// For example:
// result, err := client.RPush("my_list", []string{"a", "b", "c", "d", "e", "e", "e"})
// result, err := client.LPosCount("my_list", "e", int64(3))
// result: []api.Result[int64]{api.CreateInt64Result(4), api.CreateInt64Result(5), api.CreateInt64Result(6)}
//
//
// [valkey.io]: https://valkey.io/commands/lpos/
LPosCount(key string, element string, count int64) ([]Result[int64], error)

// Returns an array of indices of matching elements within a list based on the given options. If no match is found, an
// empty array is returned.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The name of the list.
// element - The value to search for within the list.
// count - The number of matches wanted.
// options - The LPos options.
//
// Return value:
// An array that holds the indices of the matching elements within the list.
//
// For example:
// 1. result, err := client.RPush("my_list", []string{"a", "b", "c", "d", "e", "e", "e"})
// result, err := client.LPosWithOptions("my_list", "e", int64(1), api.NewLPosOptionsBuilder().SetRank(2))
// result: []api.Result[int64]{api.CreateInt64Result(5)}
// 2. result, err := client.RPush("my_list", []string{"a", "b", "c", "d", "e", "e", "e"})
// result, err := client.LPosWithOptions(
// "my_list",
// "e",
// int64(3),
// api.NewLPosOptionsBuilder().SetRank(2).SetMaxLen(1000),
// )
// result: []api.Result[int64]{api.CreateInt64Result(5), api.CreateInt64Result(6)}
//
//
// [valkey.io]: https://valkey.io/commands/lpos/
LPosCountWithOptions(key string, element string, count int64, options *LPosOptions) ([]Result[int64], error)

// Inserts all the specified values at the tail of the list stored at key.
// elements are inserted one after the other to the tail of the list, from the leftmost element to the rightmost element.
// If key does not exist, it is created as an empty list before performing the push operation.
//
// See [valkey.io] for details.
//
// Parameters:
// key - The key of the list.
// elements - The elements to insert at the tail of the list stored at key.
//
// Return value:
// The Result[int64] containing the length of the list after the push operation.
//
// For example:
// result, err := client.RPush("my_list", []string{"a", "b", "c", "d", "e", "e", "e"})
// result.Value(): 7
// result.IsNil(): false
//
// [valkey.io]: https://valkey.io/commands/rpush/
RPush(key string, elements []string) (Result[int64], error)
}
Loading

0 comments on commit 1496c84

Please sign in to comment.