diff --git a/redis.v b/redis.v index f70a270..fbca918 100644 --- a/redis.v +++ b/redis.v @@ -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') diff --git a/redis_test.v b/redis_test.v index 4388b52..2ba4fb1 100644 --- a/redis_test.v +++ b/redis_test.v @@ -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 { @@ -707,4 +798,4 @@ fn helper_rpop_key_not_found(mut redis Redis, key string) bool { } } return false -} +} \ No newline at end of file