diff --git a/examples/threads.rs b/examples/threads.rs index b812182f..78023627 100644 --- a/examples/threads.rs +++ b/examples/threads.rs @@ -12,7 +12,7 @@ fn threads(_: &Context, _args: Vec) -> RedisResult { loop { let ctx = thread_ctx.lock(); - ctx.call("INCR", &["threads"]).unwrap(); + ctx.call("INCR", &[&ctx.create_string("threads")]).unwrap(); // release the lock as soon as we're done accessing redis memory drop(ctx); thread::sleep(Duration::from_millis(1000)); diff --git a/src/context/mod.rs b/src/context/mod.rs index 1e64e2ef..0d07a375 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -95,14 +95,8 @@ impl Context { } } - pub fn call(&self, command: &str, args: &[&str]) -> RedisResult { - let terminated_args: Vec = args - .iter() - .map(|s| RedisString::create(self.ctx, s)) - .collect(); - - let inner_args: Vec<*mut raw::RedisModuleString> = - terminated_args.iter().map(|s| s.inner).collect(); + pub fn call(&self, command: &str, args: &[&RedisString]) -> RedisResult { + let inner_args: Vec<*mut raw::RedisModuleString> = args.iter().map(|s| s.inner).collect(); let cmd = CString::new(command).unwrap(); let reply: *mut raw::RedisModuleCallReply = unsafe { @@ -112,7 +106,7 @@ impl Context { cmd.as_ptr(), raw::FMT, inner_args.as_ptr() as *mut c_char, - terminated_args.len(), + args.len(), ) }; let result = Self::parse_call_reply(reply); @@ -346,7 +340,7 @@ impl Context { } _ => { // Call "info server" - if let Ok(info) = self.call("info", &["server"]) { + if let Ok(info) = self.call("info", &[&self.create_string("server")]) { Context::version_from_info(info) } else { Err(RedisError::Str("Error calling \"info server\"")) diff --git a/src/key.rs b/src/key.rs index ef18f9a9..96c2ceea 100644 --- a/src/key.rs +++ b/src/key.rs @@ -1,7 +1,7 @@ use std::convert::TryFrom; +use std::ffi::{CString, NulError}; use std::os::raw::c_void; use std::ptr; -use std::str::Utf8Error; use std::time::Duration; use libc::size_t; @@ -75,11 +75,12 @@ impl RedisKey { self.key_inner == null_key } - pub fn read(&self) -> Result, RedisError> { + pub fn read(&self) -> Result, RedisError> { let val = if self.is_null() { None } else { - Some(read_key(self.key_inner)?) + let (cstr, length) = read_key(self.key_inner)?; + Some(RedisString::create_from_cstring(self.ctx, &cstr, length)) }; Ok(val) } @@ -156,8 +157,11 @@ impl RedisKeyWritable { } */ - pub fn read(&self) -> Result, RedisError> { - Ok(Some(read_key(self.key_inner)?)) + pub fn read(&self) -> Result, RedisError> { + let (cstr, length) = read_key(self.key_inner)?; + Ok(Some(RedisString::create_from_cstring( + self.ctx, &cstr, length, + ))) } #[allow(clippy::must_use_candidate)] @@ -253,8 +257,7 @@ impl RedisKeyWritable { } } - pub fn write(&self, val: &str) -> RedisResult { - let val_str = RedisString::create(self.ctx, val); + pub fn write(&self, val_str: &RedisString) -> RedisResult { match raw::string_set(self.key_inner, val_str.inner) { raw::Status::Ok => REDIS_OK, raw::Status::Err => Err(RedisError::Str("Error while setting key")), @@ -453,12 +456,13 @@ impl Drop for RedisKeyWritable { } } -fn read_key(key: *mut raw::RedisModuleKey) -> Result { +fn read_key(key: *mut raw::RedisModuleKey) -> Result<(CString, size_t), NulError> { let mut length: size_t = 0; - from_byte_string( + let cstr = from_byte_string( raw::string_dma(key, &mut length, raw::KeyMode::READ), length, - ) + )?; + Ok((cstr, length)) } /// Get an arbitrary number of hash fields from a key by batching calls diff --git a/src/lib.rs b/src/lib.rs index 61c89025..a5cf64a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ //#![allow(dead_code)] pub use crate::context::InfoContext; +use std::ffi::{CString, NulError}; use std::os::raw::c_char; -use std::str::Utf8Error; use strum_macros::AsRefStr; extern crate num_traits; @@ -52,14 +52,14 @@ pub enum LogLevel { Warning, } -fn from_byte_string(byte_str: *const c_char, length: size_t) -> Result { +fn from_byte_string(byte_str: *const c_char, length: size_t) -> Result { let mut vec_str: Vec = Vec::with_capacity(length as usize); for j in 0..length { let byte = unsafe { *byte_str.add(j) } as u8; vec_str.insert(j, byte); } - String::from_utf8(vec_str).map_err(|e| e.utf8_error()) + CString::new(vec_str) } pub fn base_info_func( diff --git a/src/redismodule.rs b/src/redismodule.rs index 1b87d494..74826335 100644 --- a/src/redismodule.rs +++ b/src/redismodule.rs @@ -1,6 +1,6 @@ use std::borrow::Borrow; use std::convert::TryFrom; -use std::ffi::CString; +use std::ffi::{CStr, CString}; use std::fmt; use std::fmt::Display; use std::os::raw::{c_char, c_int, c_void}; @@ -103,7 +103,19 @@ impl RedisString { #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn create(ctx: *mut raw::RedisModuleCtx, s: &str) -> Self { let str = CString::new(s).unwrap(); - let inner = unsafe { raw::RedisModule_CreateString.unwrap()(ctx, str.as_ptr(), s.len()) }; + Self::create_from_cstring(ctx, &str, s.len()) + } + + pub fn create_from_cstring(ctx: *mut raw::RedisModuleCtx, s: &CStr, len: usize) -> Self { + let inner = unsafe { raw::RedisModule_CreateString.unwrap()(ctx, s.as_ptr(), len) }; + + Self { ctx, inner } + } + + pub fn create_from_bytes(ctx: *mut raw::RedisModuleCtx, s: &[u8]) -> Self { + let inner = unsafe { + raw::RedisModule_CreateString.unwrap()(ctx, s.as_ptr() as *const i8, s.len()) + }; Self { ctx, inner } } @@ -257,6 +269,48 @@ impl From for String { } } +trait IntoRedisString { + fn into_redis_string(self, ctx: *mut raw::RedisModuleCtx) -> RedisString; +} + +macro_rules! generate_into_redis_string { + ($($t: ty) *) => { + $( + impl IntoRedisString for $t { + fn into_redis_string(self, ctx: *mut raw::RedisModuleCtx) -> RedisString { + RedisString::create(ctx, &self.to_string()) + } + } + )* + }; +} + +impl IntoRedisString for &str { + fn into_redis_string(self, ctx: *mut raw::RedisModuleCtx) -> RedisString { + RedisString::create(ctx, &self) + } +} + +impl IntoRedisString for String { + fn into_redis_string(self, ctx: *mut raw::RedisModuleCtx) -> RedisString { + RedisString::create(ctx, &self) + } +} + +impl IntoRedisString for Vec { + fn into_redis_string(self, ctx: *mut raw::RedisModuleCtx) -> RedisString { + RedisString::create_from_bytes(ctx, &self) + } +} + +impl IntoRedisString for &[u8] { + fn into_redis_string(self, ctx: *mut raw::RedisModuleCtx) -> RedisString { + RedisString::create_from_bytes(ctx, self) + } +} + +generate_into_redis_string!(bool u8 i8 u16 i16 u32 i32 u64 i64 usize isize); + /////////////////////////////////////////////////// #[derive(Debug)]