Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add support for Redis hash commands #27

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions redis.v
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,90 @@ pub fn (mut r Redis) renamenx(key string, newkey string) !int {
return parse_int(res)
}

// Hash
pub fn (mut r Redis) hset(key string, field string, value string) !int {
res := r.redis_transaction('HSET "$key" "$field" "$value"\r\n')!
trimmed_response := res.trim_space().replace(':', '')
return strconv.atoi(trimmed_response) or { return error('Failed to parse HSET response: $trimmed_response') }
}

pub fn (mut r Redis) hget(key string, field string) !string {
res := r.redis_transaction('HGET "${key}" "${field}"\r\n')!
len := parse_int(res)
if len == -1 {
return error('field not found')
}
return r.socket.read_line()[0..len]
}

pub fn (mut r Redis) hdel(key string, field string) !int {
res := r.redis_transaction('HDEL "${key}" "${field}"\r\n')!
trimmed_response := res.trim_space().replace(':', '')
return strconv.atoi(trimmed_response)
}

pub fn (mut r Redis) hexists(key string, field string) !bool {
res := r.redis_transaction('HEXISTS "${key}" "${field}"\r\n')!
trimmed_response := res.trim_space().replace(':', '')
exists := strconv.atoi(trimmed_response) or { return error('Failed to parse HEXISTS response') }
return exists == 1
}

fn (mut r Redis) redis_transaction_multi(message string) !string {
r.socket.write_string(message) or { return err }

mut response := ''
// Initial read to get the type and size of the response
line := r.socket.read_line()
response += line + '\r\n'

// If the response indicates an array, continue reading the specified number of lines
if line.starts_with('*') {
// Parse the number of elements expected
num_elements := line[1..].int()

// Read each element of the array
for _ in 0 .. num_elements {
// Each element consists of a size line followed by the actual data line
size_line := r.socket.read_line()
data_line := r.socket.read_line()
response += size_line + '\r\n' + data_line + '\r\n'
}
}

return response
}

pub fn (mut r Redis) hgetall(key string) !map[string]string {
response := r.redis_transaction_multi('HGETALL "$key"\r\n')!
lines := response.split_into_lines()
mut result := map[string]string{}
mut i := 1

for i < lines.len {
line := lines[i]

if line == "" || line.starts_with('$') {
i++
continue
}
field := line
i++

for i < lines.len && (lines[i] == "" || lines[i].starts_with('$')) {
i++
}
if i < lines.len {
value := lines[i]
result[field] = value
}

i++
}

return result
}

pub fn (mut r Redis) flushall() bool {
res := r.redis_transaction('FLUSHALL\r\n') or { return false }
return res.starts_with('+OK')
Expand Down
93 changes: 92 additions & 1 deletion redis_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -631,6 +631,97 @@ fn test_renamenx() {
assert r3 == 0
}

fn test_hset() {
mut redis := setup()
defer {
cleanup(mut redis)
}
result := redis.hset('test_hash', 'field', 'value') or {
eprintln('Failed to execute hset: $err')
assert false
return
}
assert result == 1 || result == 0
}
fn test_hget() {
mut redis := setup()
defer {
cleanup(mut redis)
}
redis.hset('test_hash', 'field', 'value') or {
eprintln('Failed to set value for test_hget: $err')
assert false
return
}
value := redis.hget('test_hash', 'field') or {
eprintln('Failed to get value for test_hget: $err')
assert false
return
}
assert value == 'value'
}

fn test_hdel() {
mut redis := setup()
defer {
cleanup(mut redis)
}
redis.hset('test_hash', 'field', 'value') or {
eprintln('Failed to set value for test_hdel: $err')
assert false
return
}
result := redis.hdel('test_hash', 'field') or {
eprintln('Failed to delete field for test_hdel: $err')
assert false
return
}
assert result == 1
}

fn test_hexists() {
mut redis := setup()
defer {
cleanup(mut redis)
}
redis.hset('test_hash', 'field', 'value') or {
eprintln('Failed to set value for test_hexists: $err')
assert false
return
}
exists := redis.hexists('test_hash', 'field') or {
eprintln('Failed to check existence for test_hexists: $err')
assert false
return
}
assert exists
}

fn test_hgetall() {
mut redis := setup()
defer {
cleanup(mut redis)
}
redis.hset('test_hash_getall', 'field1', 'value1') or {
eprintln('Failed to set field1 for test_hgetall: $err')
assert false
return
}
redis.hset('test_hash_getall', 'field2', 'value2') or {
eprintln('Failed to set field2 for test_hgetall: $err')
assert false
return
}
all_fields := redis.hgetall('test_hash_getall') or {
eprintln('Failed to get all fields for test_hgetall: $err')
assert false
return
}
assert all_fields.len == 2
assert all_fields['field1'] == 'value1'
assert all_fields['field2'] == 'value2'
}

fn test_flushall() {
mut redis := setup()
defer {
Expand Down Expand Up @@ -707,4 +798,4 @@ fn helper_rpop_key_not_found(mut redis Redis, key string) bool {
}
}
return false
}
}