From b5d53562cf814b9551f9f752729341a6f7a837d6 Mon Sep 17 00:00:00 2001 From: Lucas Jahier Date: Fri, 22 Mar 2024 16:43:40 +0100 Subject: [PATCH 1/2] feat: add basic hash commands --- redis.v | 31 ++++++++++++++++++++++- redis_test.v | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 3 deletions(-) diff --git a/redis.v b/redis.v index f70a270..08af82f 100644 --- a/redis.v +++ b/redis.v @@ -1,4 +1,4 @@ -module redis +module vredis import net import strconv @@ -318,6 +318,35 @@ 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 +} + pub fn (mut r Redis) flushall() bool { res := r.redis_transaction('FLUSHALL\r\n') or { return false } return res.starts_with('+OK') diff --git a/redis_test.v b/redis_test.v index 4388b52..2ae4afb 100644 --- a/redis_test.v +++ b/redis_test.v @@ -1,4 +1,4 @@ -module redis +module vredis fn setup() Redis { redis := connect(ConnOpts{}) or { panic(err) } @@ -631,6 +631,73 @@ 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 + } + // Checking if the field was set or already existed; both are valid outcomes for this test + 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_flushall() { mut redis := setup() defer { @@ -707,4 +774,4 @@ fn helper_rpop_key_not_found(mut redis Redis, key string) bool { } } return false -} +} \ No newline at end of file From ffb02d43ece6b4b11aca469cfd8776bf48743e5a Mon Sep 17 00:00:00 2001 From: Lucas Jahier Date: Fri, 22 Mar 2024 17:51:30 +0100 Subject: [PATCH 2/2] feat: add hgetall command --- redis.v | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++- redis_test.v | 28 ++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/redis.v b/redis.v index 08af82f..fbca918 100644 --- a/redis.v +++ b/redis.v @@ -1,4 +1,4 @@ -module vredis +module redis import net import strconv @@ -347,6 +347,61 @@ pub fn (mut r Redis) hexists(key string, field string) !bool { 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') diff --git a/redis_test.v b/redis_test.v index 2ae4afb..2ba4fb1 100644 --- a/redis_test.v +++ b/redis_test.v @@ -1,4 +1,4 @@ -module vredis +module redis fn setup() Redis { redis := connect(ConnOpts{}) or { panic(err) } @@ -641,7 +641,6 @@ fn test_hset() { assert false return } - // Checking if the field was set or already existed; both are valid outcomes for this test assert result == 1 || result == 0 } fn test_hget() { @@ -698,6 +697,31 @@ fn test_hexists() { 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 {