Skip to content

Commit

Permalink
Added hash_get macro to allow getting multiple hash fields at once
Browse files Browse the repository at this point in the history
  • Loading branch information
Slava Koyfman committed Sep 30, 2020
1 parent 751bbd1 commit 4c70b21
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
44 changes: 44 additions & 0 deletions src/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,28 @@ impl RedisKey {
};
Ok(val)
}

/// Returns the internal `*mut raw::RedisModuleCtx` for this key
///
/// # Safety
///
/// This is intended for use by macros only. It returns a raw pointer to internal
/// state. Modification or incorrect usage of this value may cause undefined
/// behaviour.
pub unsafe fn get_ctx(&self) -> *mut raw::RedisModuleCtx {
self.ctx
}

/// Returns the internal `*mut raw::RedisModuleKey` for this key
///
/// # Safety
///
/// This is intended for use by macros only. It returns a raw pointer to internal
/// state. Modification or incorrect usage of this value may cause undefined
/// behaviour.
pub unsafe fn get_inner(&self) -> *mut raw::RedisModuleKey {
self.key_inner
}
}

impl Drop for RedisKey {
Expand Down Expand Up @@ -220,6 +242,28 @@ impl RedisKeyWritable {

status.into()
}

/// Returns the internal `*mut raw::RedisModuleCtx` for this key
///
/// # Safety
///
/// This is intended for use by macros only. It returns a raw pointer to internal
/// state. Modification or incorrect usage of this value may cause undefined
/// behaviour.
pub unsafe fn get_ctx(&self) -> *mut raw::RedisModuleCtx {
self.ctx
}

/// Returns the internal `*mut raw::RedisModuleKey` for this key
///
/// # Safety
///
/// This is intended for use by macros only. It returns a raw pointer to internal
/// state. Modification or incorrect usage of this value may cause undefined
/// behaviour.
pub unsafe fn get_inner(&self) -> *mut raw::RedisModuleKey {
self.key_inner
}
}

impl From<raw::Status> for Result<(), RedisError> {
Expand Down
81 changes: 81 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,3 +128,84 @@ macro_rules! redis_module {
}
}
}

/// Gets one or more fields from a hash value, and returns them in a
/// `HashMap<String, RedisString>`. The field names may be provided as string slices
/// or as `String` objects. The returned HashMap will contain an entry for each field
/// that exists in the key. If the key itself does not exist, the HashMap will be empty.
///
/// # Examples
///
/// ```
/// let k: RedisKey = ctx.open_key("config");
/// let cfg: HashMap<String, RedisString> =
/// hash_get!(k, "hostname", "port");
/// let hostname: &str = cfg.get("hostname")
/// .map_or("localhost", |rs| rs.try_as_str().unwrap());
/// let port: &str = cfg.get("port")
/// .map_or("443", |rs| rs.try_as_str().unwrap());
/// ```
#[macro_export]
macro_rules! hash_get {
(@replace_expr $_t:tt $sub:expr) => { $sub };
(@count_tts $($tts:tt)*) => { 0usize $(+ hash_get!(@replace_expr $tts 1usize))* };

(@call ($key:expr) ($iter:ident) () -> ($($body:tt)*)) => {
unsafe {
use redis_module::redisraw::bindings::{RedisModule_HashGet, REDISMODULE_HASH_CFIELDS};
use std::ffi::CString;

RedisModule_HashGet.unwrap()(
$key,
REDISMODULE_HASH_CFIELDS as i32,
$($body)*
std::ptr::null::<std::os::raw::c_char>()
)
}
};
(@call ($key:expr) ($iter:ident) ($field:tt $($tail:tt)*) -> ($($body:tt)*)) => {
hash_get!(@call ($key) ($iter) ($($tail)*) ->
($($body)*
CString::new($field).unwrap().as_ptr(),
$iter.next().unwrap(),)
)
};

($key:ident, $($field:tt),*) => {
if !$key.is_null() {
use redis_module::raw::{RedisModuleString, Status};
use redis_module::redisraw::bindings::{RedisModule_HashGet, REDISMODULE_HASH_CFIELDS};
use redis_module::{RedisString, RedisError};
use std::collections::HashMap;

const LEN: usize = hash_get!(@count_tts $($field)*);
let mut values: [*mut RedisModuleString; LEN] = [std::ptr::null_mut(); LEN];
let res = Status::from({
let mut ivalues = values.iter_mut();
let key_inner = unsafe { $key.get_inner() };
hash_get!(@call (key_inner) (ivalues) ($($field)*) -> ())
});
if res == Status::Ok {
let mut map: HashMap<String, RedisString> =
HashMap::with_capacity(LEN);
{
let key_ctx = unsafe { $key.get_ctx() };
let mut ivalues = values.iter_mut();
unsafe {
$(
if let Some(p) = ivalues.next().unwrap().as_mut() {
map.insert($field.to_string(), RedisString::new(key_ctx, p));
}
)*
}
}
Ok(map)
} else {
Err(RedisError::Str("ERR key is not a hash value"))
}
} else {
use redis_module::RedisString;
Ok(HashMap::<String, RedisString>::with_capacity(0))
}
};
}

0 comments on commit 4c70b21

Please sign in to comment.