From 65f619a06a4b3757616f3b28af4795d8360d46db Mon Sep 17 00:00:00 2001 From: Victor Polevoy Date: Wed, 22 Nov 2023 15:34:21 +0100 Subject: [PATCH 1/4] Refactor the FFI calls --- Cargo.toml | 9 +- examples/info_handler_macro.rs | 2 + examples/open_key_with_flags.rs | 3 +- examples/proc_macro_commands.rs | 3 +- examples/test_helper.rs | 3 + redismodule-rs-macros/Cargo.toml | 6 +- src/context/call_reply.rs | 9 +- src/context/commands.rs | 4 +- src/context/mod.rs | 74 ++-- src/context/timer.rs | 8 +- src/key.rs | 17 +- src/lib.rs | 1 + src/raw.rs | 558 ++++++++++++++++++++----------- src/stream.rs | 37 +- 14 files changed, 428 insertions(+), 306 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a9b1c764..7dd39967 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -119,16 +119,17 @@ crate-type = ["cdylib"] [dependencies] bitflags = "2" libc = "0.2" -enum-primitive-derive = "^0.1" -num-traits = "^0.2" +enum-primitive-derive = "0.3" +num-traits = "0.2" regex = "1" -strum_macros = "0.24" +strum_macros = "0.25" backtrace = "0.3" linkme = "0.3" serde = { version = "1", features = ["derive"] } nix = "0.26" cfg-if = "1" redis-module-macros-internals = { path = "./redismodule-rs-macros-internals" } +redis-module-macros = { path = "./redismodule-rs-macros"} log = "0.4" [dev-dependencies] @@ -139,7 +140,7 @@ redis-module-macros = { path = "./redismodule-rs-macros"} redis-module = { path = "./", default-features = false, features = ["min-redis-compatibility-version-7-2"] } [build-dependencies] -bindgen = "0.66" +bindgen = "0.68" cc = "1" [features] diff --git a/examples/info_handler_macro.rs b/examples/info_handler_macro.rs index fb38023c..7da6ba15 100644 --- a/examples/info_handler_macro.rs +++ b/examples/info_handler_macro.rs @@ -2,6 +2,8 @@ use redis_module::{redis_module, RedisResult}; use redis_module::{InfoContext, Status}; use redis_module_macros::info_command_handler; +// The deprecated methods are allowed since this is just an example. +#[allow(deprecated)] #[info_command_handler] fn add_info(ctx: &InfoContext, _for_crash_report: bool) -> RedisResult<()> { if ctx.add_info_section(Some("info")) == Status::Ok { diff --git a/examples/open_key_with_flags.rs b/examples/open_key_with_flags.rs index 594116f2..abc2c5c7 100644 --- a/examples/open_key_with_flags.rs +++ b/examples/open_key_with_flags.rs @@ -1,6 +1,5 @@ use redis_module::{ - key::KeyFlags, raw, redis_module, Context, NextArg, RedisError, RedisResult, RedisString, - RedisValue, + key::KeyFlags, redis_module, Context, NextArg, RedisError, RedisResult, RedisString, RedisValue, }; use redis_module_macros::command; diff --git a/examples/proc_macro_commands.rs b/examples/proc_macro_commands.rs index fe433f7c..9721b9d3 100644 --- a/examples/proc_macro_commands.rs +++ b/examples/proc_macro_commands.rs @@ -1,8 +1,7 @@ use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet}; use redis_module::RedisError; -use redis_module::{redis_module, Context, RedisResult, RedisString, RedisValue}; -use redis_module_macros::{command, RedisValue}; +use redis_module::{command, redis_module, Context, RedisResult, RedisString, RedisValue}; #[derive(RedisValue)] struct RedisValueDeriveInner { diff --git a/examples/test_helper.rs b/examples/test_helper.rs index 79139a07..3b051672 100644 --- a/examples/test_helper.rs +++ b/examples/test_helper.rs @@ -30,6 +30,9 @@ fn test_helper_err(ctx: &Context, args: Vec) -> RedisResult { Ok(().into()) } +// This is a function to test that the deprecated API still works fine, +// there is no need to check for the deprecated calls. +#[allow(deprecated)] fn add_info(ctx: &InfoContext, _for_crash_report: bool) { if ctx.add_info_section(Some("test_helper")) == Status::Ok { ctx.add_info_field_str("field", "value"); diff --git a/redismodule-rs-macros/Cargo.toml b/redismodule-rs-macros/Cargo.toml index 08eb9293..8cb6ad51 100644 --- a/redismodule-rs-macros/Cargo.toml +++ b/redismodule-rs-macros/Cargo.toml @@ -12,11 +12,11 @@ categories = ["database", "api-bindings"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -syn = { version="1.0", features = ["full"]} -quote = "1.0" +syn = { version = "1", features = ["full"]} +quote = "1" proc-macro2 = "1" serde = { version = "1", features = ["derive"] } -serde_syn = "0.1.0" +serde_syn = "0.1" [lib] name = "redis_module_macros" diff --git a/src/context/call_reply.rs b/src/context/call_reply.rs index 6c739b6f..9000ba1f 100644 --- a/src/context/call_reply.rs +++ b/src/context/call_reply.rs @@ -753,11 +753,10 @@ where lock_indicator: &LockIndicator, ) -> Status { let mut callback: *mut C = std::ptr::null_mut(); - let res = unsafe { - RedisModule_CallReplyPromiseAbort - .expect("RedisModule_CallReplyPromiseAbort is expected to be available if we got a promise call reply") - (self.reply.as_ptr(), &mut callback as *mut *mut C as *mut *mut c_void) - }.into(); + let res = crate::raw::call_reply_promise_abort( + self.reply.as_ptr(), + &mut callback as *mut *mut C as *mut *mut c_void, + ); if !callback.is_null() { unsafe { deallocate_pointer(callback) }; diff --git a/src/context/commands.rs b/src/context/commands.rs index 89832cea..dfc80ef5 100644 --- a/src/context/commands.rs +++ b/src/context/commands.rs @@ -454,12 +454,12 @@ api! {[ // the only CString pointers which are not freed are those of the key_specs, lets free them here. key_specs.into_iter().for_each(|v|{ if !v.notes.is_null() { - unsafe{CString::from_raw(v.notes as *mut c_char)}; + unsafe{ drop(CString::from_raw(v.notes as *mut c_char)) }; } if v.begin_search_type == raw::RedisModuleKeySpecBeginSearchType_REDISMODULE_KSPEC_BS_KEYWORD { let keyword = unsafe{v.bs.keyword.keyword}; if !keyword.is_null() { - unsafe{CString::from_raw(v.bs.keyword.keyword as *mut c_char)}; + unsafe{ drop(CString::from_raw(v.bs.keyword.keyword as *mut c_char)) }; } } }); diff --git a/src/context/mod.rs b/src/context/mod.rs index cb6cde01..5d7e4eba 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -476,8 +476,7 @@ impl Context { #[allow(clippy::must_use_candidate)] pub fn reply_error_string(&self, s: &str) -> raw::Status { - let msg = Self::str_as_legal_resp_string(s); - unsafe { raw::RedisModule_ReplyWithError.unwrap()(self.ctx, msg.as_ptr()).into() } + raw::reply_with_error(self.ctx, s) } pub fn reply_with_key(&self, result: RedisValueKey) -> raw::Status { @@ -590,14 +589,14 @@ impl Context { Ok(RedisValue::StaticError(s)) => self.reply_error_string(s), - Err(RedisError::WrongArity) => unsafe { + Err(RedisError::WrongArity) => { if self.is_keys_position_request() { // We can't return a result since we don't have a client raw::Status::Err } else { - raw::RedisModule_WrongArity.unwrap()(self.ctx).into() + raw::wrong_arity(self.ctx) } - }, + } Err(RedisError::WrongType) => { self.reply_error_string(RedisError::WrongType.to_string().as_str()) @@ -781,53 +780,38 @@ impl Context { key_name: &RedisString, permissions: &AclPermissions, ) -> Result<(), RedisError> { - let user = unsafe { raw::RedisModule_GetModuleUserFromUserName.unwrap()(user_name.inner) }; + let user = raw::get_module_user_from_user_name(user_name.inner); if user.is_null() { return Err(RedisError::Str("User does not exists or disabled")); } - let acl_permission_result: raw::Status = unsafe { - raw::RedisModule_ACLCheckKeyPermissions.unwrap()( - user, - key_name.inner, - permissions.bits(), - ) - } - .into(); + let acl_permission_result = + raw::acl_check_key_permissions(user, key_name.inner, permissions.bits()); unsafe { raw::RedisModule_FreeModuleUser.unwrap()(user) }; let acl_permission_result: Result<(), &str> = acl_permission_result.into(); acl_permission_result.map_err(|_e| RedisError::Str("User does not have permissions on key")) } - api!( - [RedisModule_AddPostNotificationJob], - /// When running inside a key space notification callback, it is dangerous and highly discouraged to perform any write - /// operation. In order to still perform write actions in this scenario, Redis provides this API ([add_post_notification_job]) - /// that allows to register a job callback which Redis will call when the following condition holds: - /// - /// 1. It is safe to perform any write operation. - /// 2. The job will be called atomically along side the key space notification. - /// - /// Notice, one job might trigger key space notifications that will trigger more jobs. - /// This raises a concerns of entering an infinite loops, we consider infinite loops - /// as a logical bug that need to be fixed in the module, an attempt to protect against - /// infinite loops by halting the execution could result in violation of the feature correctness - /// and so Redis will make no attempt to protect the module from infinite loops. - pub fn add_post_notification_job( - &self, - callback: F, - ) -> Status { - let callback = Box::into_raw(Box::new(Some(callback))); - unsafe { - RedisModule_AddPostNotificationJob( - self.ctx, - Some(post_notification_job::), - callback as *mut c_void, - Some(post_notification_job_free_callback::), - ) - } - .into() - } - ); + /// When running inside a key space notification callback, it is dangerous and highly discouraged to perform any write + /// operation. In order to still perform write actions in this scenario, Redis provides this API ([add_post_notification_job]) + /// that allows to register a job callback which Redis will call when the following condition holds: + /// + /// 1. It is safe to perform any write operation. + /// 2. The job will be called atomically along side the key space notification. + /// + /// Notice, one job might trigger key space notifications that will trigger more jobs. + /// This raises a concerns of entering an infinite loops, we consider infinite loops + /// as a logical bug that need to be fixed in the module, an attempt to protect against + /// infinite loops by halting the execution could result in violation of the feature correctness + /// and so Redis will make no attempt to protect the module from infinite loops. + pub fn add_post_notification_job(&self, callback: F) -> Status { + let callback = Box::into_raw(Box::new(Some(callback))); + raw::add_post_notification_job( + self.ctx, + Some(post_notification_job::), + callback as *mut c_void, + Some(post_notification_job_free_callback::), + ) + } api!( [RedisModule_AvoidReplicaTraffic], @@ -873,7 +857,7 @@ impl Context { } extern "C" fn post_notification_job_free_callback(pd: *mut c_void) { - unsafe { Box::from_raw(pd as *mut Option) }; + unsafe { drop(Box::from_raw(pd as *mut Option)) }; } extern "C" fn post_notification_job( diff --git a/src/context/timer.rs b/src/context/timer.rs index addbd384..96e1199d 100644 --- a/src/context/timer.rs +++ b/src/context/timer.rs @@ -60,8 +60,7 @@ impl Context { pub fn stop_timer(&self, timer_id: RedisModuleTimerID) -> Result { let mut data: *mut c_void = std::ptr::null_mut(); - let status: raw::Status = - unsafe { raw::RedisModule_StopTimer.unwrap()(self.ctx, timer_id, &mut data) }.into(); + let status = raw::stop_timer(self.ctx, timer_id, &mut data); if status != raw::Status::Ok { return Err(RedisError::Str( @@ -86,10 +85,7 @@ impl Context { let mut remaining: u64 = 0; let mut data: *mut c_void = std::ptr::null_mut(); - let status: raw::Status = unsafe { - raw::RedisModule_GetTimerInfo.unwrap()(self.ctx, timer_id, &mut remaining, &mut data) - } - .into(); + let status = raw::get_timer_info(self.ctx, timer_id, &mut remaining, &mut data); if status != raw::Status::Ok { return Err(RedisError::Str( diff --git a/src/key.rs b/src/key.rs index 9c4685ca..555afd47 100644 --- a/src/key.rs +++ b/src/key.rs @@ -108,7 +108,7 @@ impl RedisKey { /// Will panic if `RedisModule_KeyType` is missing in redismodule.h #[must_use] pub fn key_type(&self) -> raw::KeyType { - unsafe { raw::RedisModule_KeyType.unwrap()(self.key_inner) }.into() + raw::key_type(self.key_inner) } /// Detects whether the key pointer given to us by Redis is null. @@ -358,7 +358,7 @@ impl RedisKeyWritable { /// Will panic if `RedisModule_KeyType` is missing in redismodule.h #[must_use] pub fn key_type(&self) -> raw::KeyType { - unsafe { raw::RedisModule_KeyType.unwrap()(self.key_inner) }.into() + raw::key_type(self.key_inner) } #[must_use] @@ -402,16 +402,7 @@ impl RedisKeyWritable { pub fn set_value(&self, redis_type: &RedisType, value: T) -> Result<(), RedisError> { verify_type(self.key_inner, redis_type)?; let value = Box::into_raw(Box::new(value)).cast::(); - let status: raw::Status = unsafe { - raw::RedisModule_ModuleTypeSetValue.unwrap()( - self.key_inner, - *redis_type.raw_type.borrow(), - value, - ) - } - .into(); - - status.into() + raw::module_type_set_value(self.key_inner, *redis_type.raw_type.borrow(), value).into() } pub fn trim_stream_by_id( @@ -654,7 +645,7 @@ fn to_raw_mode(mode: KeyMode) -> raw::KeyMode { /// Will panic if `RedisModule_KeyType` or `RedisModule_ModuleTypeGetType` are missing in redismodule.h #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn verify_type(key_inner: *mut raw::RedisModuleKey, redis_type: &RedisType) -> RedisResult { - let key_type: KeyType = unsafe { raw::RedisModule_KeyType.unwrap()(key_inner) }.into(); + let key_type = raw::key_type(key_inner); if key_type != KeyType::Empty { // The key exists; check its type diff --git a/src/lib.rs b/src/lib.rs index e707737d..32ec8e1a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -50,6 +50,7 @@ pub use crate::raw::*; pub use crate::redismodule::*; use backtrace::Backtrace; use context::server_events::INFO_COMMAND_HANDLER_LIST; +pub use redis_module_macros::*; /// The detached Redis module context (the context of this module). It /// is only set to a proper value after the module is initialised via the diff --git a/src/raw.rs b/src/raw.rs index afca5ceb..8e5da690 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -7,7 +7,7 @@ extern crate libc; extern crate num_traits; use std::cmp::Ordering; -use std::ffi::{c_ulonglong, CStr, CString}; +use std::ffi::{c_ulonglong, CString}; use std::os::raw::{c_char, c_double, c_int, c_long, c_longlong, c_void}; use std::ptr; use std::slice; @@ -16,11 +16,10 @@ use crate::RedisResult; use bitflags::bitflags; use enum_primitive_derive::Primitive; use libc::size_t; -use num_traits::FromPrimitive; use crate::error::Error; pub use crate::redisraw::bindings::*; -use crate::{context::StrCallArgs, Context, RedisString}; +use crate::{context::StrCallArgs, RedisString}; use crate::{RedisBuffer, RedisError}; const GENERIC_ERROR_MESSAGE: &str = "Generic error."; @@ -40,6 +39,57 @@ bitflags! { } } +/// Gracefully wraps a call to a raw function, trying to convert the +/// result into the suitable return value in the place it is called, if +/// needed. +macro_rules! redis_call { + // Calls a raw function and simply returns the value as-is. + (raw $raw_function:ident) => { + unsafe { + $raw_function + .expect(&format!("The function {} is available.", stringify($raw_function))) + () + } + }; + + // Calls a raw function with the arguments provided, attempting to + // convert the resulting value using the [`TryInto`] trait + // automatically. + ($raw_function:ident($($args:expr),*)) => { + unsafe { + $raw_function + .expect(&format!("The function {} is available.", stringify!($raw_function))) + ($($args),*) + .try_into() + .unwrap() + } + }; + + // Calls a raw function with the arguments provided, attempting to + // convert the resulting value to the specified type using the + // [`TryFrom`] trait. + ($raw_function:ident<$typ:ty>($($args:expr),*)) => { + unsafe { + <$typ>::try_from( + $raw_function + .expect(&format!("The function {} is available.", stringify!($raw_function))) + ($($args),*) + ) + .expect(&format!("A conversion to {} being possible.", stringify!($typ))) + } + }; + + // Calls a raw function with the arguments provided, returning the + // resulting value as-is. + (raw $raw_function:ident($($args:expr),*)) => { + unsafe { + $raw_function + .expect(&format!("The function {} is available.", stringify!($raw_function))) + ($($args),*) + } + }; +} + #[derive(Primitive, Debug, PartialEq, Eq)] pub enum KeyType { Empty = REDISMODULE_KEYTYPE_EMPTY, @@ -52,12 +102,6 @@ pub enum KeyType { Stream = REDISMODULE_KEYTYPE_STREAM, } -impl From for KeyType { - fn from(v: c_int) -> Self { - Self::from_i32(v).unwrap() - } -} - #[derive(Primitive, Debug, PartialEq, Eq)] pub enum Where { ListHead = REDISMODULE_LIST_HEAD, @@ -80,12 +124,6 @@ pub enum ReplyType { VerbatimString = REDISMODULE_REPLY_VERBATIM_STRING, } -impl From for ReplyType { - fn from(v: c_int) -> Self { - Self::from_i32(v).unwrap() - } -} - #[derive(Primitive, Debug, PartialEq, Eq)] pub enum Aux { Before = REDISMODULE_AUX_BEFORE_RDB, @@ -107,12 +145,6 @@ impl From for RedisResult<()> { } } -impl From for Status { - fn from(v: c_int) -> Self { - Self::from_i32(v).unwrap() - } -} - impl From for Result<(), &str> { fn from(s: Status) -> Self { match s { @@ -208,20 +240,18 @@ pub const REDISMODULE_HASH_DELETE: *const RedisModuleString = 1 as *const RedisM #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn call_reply_type(reply: *mut RedisModuleCallReply) -> ReplyType { - unsafe { - // TODO: Cache the unwrapped functions and use them instead of unwrapping every time? - RedisModule_CallReplyType.unwrap()(reply).into() - } + // TODO: Cache the unwrapped functions and use them instead of unwrapping every time? + redis_call!(RedisModule_CallReplyType(reply)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn free_call_reply(reply: *mut RedisModuleCallReply) { - unsafe { RedisModule_FreeCallReply.unwrap()(reply) } + redis_call!(RedisModule_FreeCallReply(reply)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn call_reply_integer(reply: *mut RedisModuleCallReply) -> c_longlong { - unsafe { RedisModule_CallReplyInteger.unwrap()(reply) } + redis_call!(RedisModule_CallReplyInteger(reply)) } /// # Panics @@ -229,7 +259,7 @@ pub fn call_reply_integer(reply: *mut RedisModuleCallReply) -> c_longlong { /// Panics if the Redis server doesn't support replying with bool (since RESP3). #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn call_reply_bool(reply: *mut RedisModuleCallReply) -> bool { - (unsafe { RedisModule_CallReplyBool.unwrap()(reply) } != 0) + redis_call!(raw RedisModule_CallReplyBool(reply)) != 0 } /// # Panics @@ -237,7 +267,7 @@ pub fn call_reply_bool(reply: *mut RedisModuleCallReply) -> bool { /// Panics if the Redis server doesn't support replying with bool (since RESP3). #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn call_reply_double(reply: *mut RedisModuleCallReply) -> f64 { - unsafe { RedisModule_CallReplyDouble.unwrap()(reply) } + redis_call!(RedisModule_CallReplyDouble(reply)) } /// # Panics @@ -245,15 +275,16 @@ pub fn call_reply_double(reply: *mut RedisModuleCallReply) -> f64 { /// Panics if the Redis server doesn't support replying with bool (since RESP3). #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn call_reply_big_number(reply: *mut RedisModuleCallReply) -> Option { - unsafe { - let mut len: size_t = 0; - let reply_string: *mut u8 = - RedisModule_CallReplyBigNumber.unwrap()(reply, &mut len) as *mut u8; + let mut len: size_t = 0; + String::from_utf8({ + let reply_string = + redis_call!(raw RedisModule_CallReplyBigNumber(reply, &mut len)) as *mut u8; if reply_string.is_null() { return None; } - String::from_utf8(slice::from_raw_parts(reply_string, len).to_vec()).ok() - } + unsafe { slice::from_raw_parts(reply_string, len).to_vec() } + }) + .ok() } /// # Panics @@ -261,20 +292,38 @@ pub fn call_reply_big_number(reply: *mut RedisModuleCallReply) -> Option /// Panics if the Redis server doesn't support replying with bool (since RESP3). #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn call_reply_verbatim_string(reply: *mut RedisModuleCallReply) -> Option<(String, Vec)> { - unsafe { - let mut len: size_t = 0; - let format: *const u8 = ptr::null(); - let reply_string: *mut u8 = - RedisModule_CallReplyVerbatim.unwrap()(reply, &mut len, &mut (format as *const c_char)) - as *mut u8; - if reply_string.is_null() { - return None; - } - Some(( + let mut len: size_t = 0; + let format: *const u8 = ptr::null(); + let reply_string = redis_call!(raw RedisModule_CallReplyVerbatim(reply, &mut len, &mut (format as *const c_char))) + as *mut u8; + if reply_string.is_null() { + return None; + } + Some(unsafe { + ( String::from_utf8(slice::from_raw_parts(format, 3).to_vec()).ok()?, slice::from_raw_parts(reply_string, len).to_vec(), - )) - } + ) + }) +} + +/// Aborts the invocation of the blocking commands. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn call_reply_promise_abort( + reply: *mut RedisModuleCallReply, + private_data: *mut *mut ::std::os::raw::c_void, +) -> Status { + redis_call!(RedisModule_CallReplyPromiseAbort(reply, private_data)) +} + +macro_rules! generate_transparent_binding { + ($(#[$outer:meta])* $vis:vis $rust_name:ident => $raw_function:ident, $ret_type:ty, $(($arg:ident, $typ:ty)),*) => { + $(#[$outer])* + #[allow(clippy::not_unsafe_ptr_arg_deref)] + $vis fn $rust_name($($arg: $typ),*) -> $ret_type { + raw_call!($raw_function, $($arg),*) + } + }; } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -282,9 +331,19 @@ pub fn call_reply_array_element( reply: *mut RedisModuleCallReply, idx: usize, ) -> *mut RedisModuleCallReply { - unsafe { RedisModule_CallReplyArrayElement.unwrap()(reply, idx) } + redis_call!(RedisModule_CallReplyArrayElement(reply, idx)) } +// generate_transparent_binding!( +// /// # Panics +// /// +// /// Panics if the Redis server doesn't support replying with bool (since RESP3). +// pub call_reply_set_element => RedisModule_CallReplySetElement, +// *mut RedisModuleCallReply, +// (reply, *mut RedisModuleCallReply), +// (idx, usize) +// ); + /// # Panics /// /// Panics if the Redis server doesn't support replying with bool (since RESP3). @@ -293,7 +352,7 @@ pub fn call_reply_set_element( reply: *mut RedisModuleCallReply, idx: usize, ) -> *mut RedisModuleCallReply { - unsafe { RedisModule_CallReplySetElement.unwrap()(reply, idx) } + redis_call!(RedisModule_CallReplySetElement(reply, idx)) } /// # Panics @@ -306,37 +365,37 @@ pub fn call_reply_map_element( ) -> (*mut RedisModuleCallReply, *mut RedisModuleCallReply) { let mut key: *mut RedisModuleCallReply = ptr::null_mut(); let mut val: *mut RedisModuleCallReply = ptr::null_mut(); - unsafe { RedisModule_CallReplyMapElement.unwrap()(reply, idx, &mut key, &mut val) }; + redis_call!(raw RedisModule_CallReplyMapElement( + reply, idx, &mut key, &mut val + )); (key, val) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn call_reply_length(reply: *mut RedisModuleCallReply) -> usize { - unsafe { RedisModule_CallReplyLength.unwrap()(reply) } + redis_call!(raw RedisModule_CallReplyLength(reply)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn call_reply_string_ptr(reply: *mut RedisModuleCallReply, len: *mut size_t) -> *const c_char { - unsafe { RedisModule_CallReplyStringPtr.unwrap()(reply, len) } + redis_call!(raw RedisModule_CallReplyStringPtr(reply, len)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn call_reply_string(reply: *mut RedisModuleCallReply) -> Option { - unsafe { - let mut len: size_t = 0; - let reply_string: *mut u8 = - RedisModule_CallReplyStringPtr.unwrap()(reply, &mut len) as *mut u8; - if reply_string.is_null() { - return None; - } - String::from_utf8(slice::from_raw_parts(reply_string, len).to_vec()).ok() + let mut len: size_t = 0; + let reply_string: *mut u8 = + redis_call!(raw RedisModule_CallReplyStringPtr(reply, &mut len)) as *mut u8; + if reply_string.is_null() { + return None; } + unsafe { String::from_utf8(slice::from_raw_parts(reply_string, len).to_vec()).ok() } } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn close_key(kp: *mut RedisModuleKey) { - unsafe { RedisModule_CloseKey.unwrap()(kp) } + redis_call!(RedisModule_CloseKey(kp)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -346,7 +405,7 @@ pub fn open_key( keyname: *mut RedisModuleString, mode: KeyMode, ) -> *mut RedisModuleKey { - unsafe { RedisModule_OpenKey.unwrap()(ctx, keyname, mode.bits()).cast::() } + redis_call!(RedisModule_OpenKey(ctx, keyname, mode.bits())) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -357,103 +416,97 @@ pub(crate) fn open_key_with_flags( mode: KeyMode, flags: c_int, ) -> *mut RedisModuleKey { - unsafe { - RedisModule_OpenKey.unwrap()(ctx, keyname, mode.bits() | flags).cast::() - } + redis_call!(RedisModule_OpenKey(ctx, keyname, mode.bits() | flags)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_array(ctx: *mut RedisModuleCtx, len: c_long) -> Status { - unsafe { RedisModule_ReplyWithArray.unwrap()(ctx, len).into() } + redis_call!(RedisModule_ReplyWithArray(ctx, len)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_map(ctx: *mut RedisModuleCtx, len: c_long) -> Status { - unsafe { - RedisModule_ReplyWithMap - .map_or_else( - || RedisModule_ReplyWithArray.unwrap()(ctx, len * 2), - |f| f(ctx, len), - ) - .into() - } + unsafe { RedisModule_ReplyWithMap } + .map_or_else( + || redis_call!(RedisModule_ReplyWithArray(ctx, len * 2)), + |f| unsafe { f(ctx, len) }, + ) + .try_into() + .unwrap() } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_set(ctx: *mut RedisModuleCtx, len: c_long) -> Status { - unsafe { - RedisModule_ReplyWithSet - .map_or_else( - || RedisModule_ReplyWithArray.unwrap()(ctx, len * 2), - |f| f(ctx, len), - ) - .into() - } + unsafe { RedisModule_ReplyWithSet } + .map_or_else( + || redis_call!(RedisModule_ReplyWithArray(ctx, len * 2)), + |f| unsafe { f(ctx, len) }, + ) + .try_into() + .unwrap() } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_attribute(ctx: *mut RedisModuleCtx, len: c_long) -> Status { - unsafe { RedisModule_ReplyWithAttribute.unwrap()(ctx, len).into() } + redis_call!(RedisModule_ReplyWithAttribute(ctx, len)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] -pub fn reply_with_error(ctx: *mut RedisModuleCtx, err: *const c_char) { - unsafe { - let msg = Context::str_as_legal_resp_string(CStr::from_ptr(err).to_str().unwrap()); - RedisModule_ReplyWithError.unwrap()(ctx, msg.as_ptr()); - } +pub fn reply_with_error>(ctx: *mut RedisModuleCtx, err: S) -> Status { + let msg = CString::new(err.as_ref()).unwrap(); + redis_call!(RedisModule_ReplyWithError(ctx, msg.as_ptr())) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_null(ctx: *mut RedisModuleCtx) -> Status { - unsafe { RedisModule_ReplyWithNull.unwrap()(ctx).into() } + redis_call!(RedisModule_ReplyWithNull(ctx)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_bool(ctx: *mut RedisModuleCtx, b: c_int) -> Status { - unsafe { RedisModule_ReplyWithBool.unwrap()(ctx, b).into() } + redis_call!(RedisModule_ReplyWithBool(ctx, b)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_long_long(ctx: *mut RedisModuleCtx, ll: c_longlong) -> Status { - unsafe { RedisModule_ReplyWithLongLong.unwrap()(ctx, ll).into() } + redis_call!(RedisModule_ReplyWithLongLong(ctx, ll)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_double(ctx: *mut RedisModuleCtx, f: c_double) -> Status { - unsafe { RedisModule_ReplyWithDouble.unwrap()(ctx, f).into() } + redis_call!(RedisModule_ReplyWithDouble(ctx, f)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_string(ctx: *mut RedisModuleCtx, s: *mut RedisModuleString) -> Status { - unsafe { RedisModule_ReplyWithString.unwrap()(ctx, s).into() } + redis_call!(RedisModule_ReplyWithString(ctx, s)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_simple_string(ctx: *mut RedisModuleCtx, s: *const c_char) -> Status { - unsafe { RedisModule_ReplyWithSimpleString.unwrap()(ctx, s).into() } + redis_call!(RedisModule_ReplyWithSimpleString(ctx, s)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_string_buffer(ctx: *mut RedisModuleCtx, s: *const c_char, len: size_t) -> Status { - unsafe { RedisModule_ReplyWithStringBuffer.unwrap()(ctx, s, len).into() } + redis_call!(RedisModule_ReplyWithStringBuffer(ctx, s, len)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn reply_with_big_number(ctx: *mut RedisModuleCtx, s: *const c_char, len: size_t) -> Status { - unsafe { RedisModule_ReplyWithBigNumber.unwrap()(ctx, s, len).into() } + redis_call!(RedisModule_ReplyWithBigNumber(ctx, s, len)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -464,7 +517,7 @@ pub fn reply_with_verbatim_string( len: size_t, format: *const c_char, ) -> Status { - unsafe { RedisModule_ReplyWithVerbatimStringType.unwrap()(ctx, s, len, format).into() } + redis_call!(RedisModule_ReplyWithVerbatimStringType(ctx, s, len, format)) } // Sets the expiry on a key. @@ -473,19 +526,19 @@ pub fn reply_with_verbatim_string( #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn set_expire(key: *mut RedisModuleKey, expire: c_longlong) -> Status { - unsafe { RedisModule_SetExpire.unwrap()(key, expire).into() } + redis_call!(RedisModule_SetExpire(key, expire)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn string_dma(key: *mut RedisModuleKey, len: *mut size_t, mode: KeyMode) -> *mut c_char { - unsafe { RedisModule_StringDMA.unwrap()(key, len, mode.bits()) } + redis_call!(RedisModule_StringDMA(key, len, mode.bits())) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn string_truncate(key: *mut RedisModuleKey, new_len: size_t) -> Status { - unsafe { RedisModule_StringTruncate.unwrap()(key, new_len).into() } + redis_call!(RedisModule_StringTruncate(key, new_len)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -508,17 +561,17 @@ where let mut vi = values.iter_mut(); macro_rules! rm { - () => { unsafe { - RedisModule_HashGet.unwrap()(key, REDISMODULE_HASH_CFIELDS as i32, - ptr::null::()) - }}; - ($($args:expr)*) => { unsafe { - RedisModule_HashGet.unwrap()( + () => { + redis_call!(RedisModule_HashGet(key, REDISMODULE_HASH_CFIELDS as i32, + ptr::null::())) + }; + ($($args:expr)*) => { + redis_call!(RedisModule_HashGet( key, REDISMODULE_HASH_CFIELDS as i32, $($args),*, ptr::null::() - ) - }}; + )) + }; } macro_rules! f { () => { @@ -535,7 +588,7 @@ where // to modules. Unfortunately there's no straightforward or portable way of calling a // a varargs function with a variable number of arguments that is determined at runtime. // See also the following Redis ticket: https://github.com/redis/redis/issues/7860 - let res = Status::from(match fields.len() { + let res = match fields.len() { 0 => rm! {}, 1 => rm! {f!() v!()}, 2 => rm! {f!() v!() f!() v!()}, @@ -568,7 +621,7 @@ where f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() }, _ => panic!("Unsupported length"), - }); + }; match res { Status::Ok => Ok(()), @@ -581,16 +634,13 @@ where pub fn hash_set(key: *mut RedisModuleKey, field: &str, value: *mut RedisModuleString) -> Status { let field = CString::new(field).unwrap(); - unsafe { - RedisModule_HashSet.unwrap()( - key, - REDISMODULE_HASH_CFIELDS as i32, - field.as_ptr(), - value, - ptr::null::(), - ) - .into() - } + redis_call!(RedisModule_HashSet( + key, + REDISMODULE_HASH_CFIELDS as i32, + field.as_ptr(), + value, + ptr::null::() + )) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -602,16 +652,13 @@ pub fn hash_del(key: *mut RedisModuleKey, field: &str) -> Status { // Support to pass multiple fields is desired but is complicated. // See hash_get_multi() and https://github.com/redis/redis/issues/7860 - unsafe { - RedisModule_HashSet.unwrap()( - key, - REDISMODULE_HASH_CFIELDS as i32, - field.as_ptr(), - REDISMODULE_HASH_DELETE, - ptr::null::(), - ) - .into() - } + redis_call!(RedisModule_HashSet( + key, + REDISMODULE_HASH_CFIELDS as i32, + field.as_ptr(), + REDISMODULE_HASH_DELETE, + ptr::null::() + )) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -621,50 +668,50 @@ pub fn list_push( list_where: Where, element: *mut RedisModuleString, ) -> Status { - unsafe { RedisModule_ListPush.unwrap()(key, list_where as i32, element).into() } + redis_call!(RedisModule_ListPush(key, list_where as i32, element)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn list_pop(key: *mut RedisModuleKey, list_where: Where) -> *mut RedisModuleString { - unsafe { RedisModule_ListPop.unwrap()(key, list_where as i32) } + redis_call!(RedisModule_ListPop(key, list_where as i32)) } // Returns pointer to the C string, and sets len to its length #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn string_ptr_len(s: *const RedisModuleString, len: *mut size_t) -> *const c_char { - unsafe { RedisModule_StringPtrLen.unwrap()(s, len) } + redis_call!(RedisModule_StringPtrLen(s, len)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn string_retain_string(ctx: *mut RedisModuleCtx, s: *mut RedisModuleString) { - unsafe { RedisModule_RetainString.unwrap()(ctx, s) } + redis_call!(RedisModule_RetainString(ctx, s)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn string_to_longlong(s: *const RedisModuleString, len: *mut i64) -> Status { - unsafe { RedisModule_StringToLongLong.unwrap()(s, len).into() } + redis_call!(RedisModule_StringToLongLong(s, len)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn string_to_double(s: *const RedisModuleString, len: *mut f64) -> Status { - unsafe { RedisModule_StringToDouble.unwrap()(s, len).into() } + redis_call!(RedisModule_StringToDouble(s, len)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn string_set(key: *mut RedisModuleKey, s: *mut RedisModuleString) -> Status { - unsafe { RedisModule_StringSet.unwrap()(key, s).into() } + redis_call!(RedisModule_StringSet(key, s)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] #[inline] pub fn replicate_verbatim(ctx: *mut RedisModuleCtx) -> Status { - unsafe { RedisModule_ReplicateVerbatim.unwrap()(ctx).into() } + redis_call!(RedisModule_ReplicateVerbatim(ctx)) } fn load(rdb: *mut RedisModuleIO, f: F) -> Result @@ -681,29 +728,27 @@ where #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn load_unsigned(rdb: *mut RedisModuleIO) -> Result { - unsafe { load(rdb, |rdb| RedisModule_LoadUnsigned.unwrap()(rdb)) } + load(rdb, |rdb| redis_call!(RedisModule_LoadUnsigned(rdb))) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn load_signed(rdb: *mut RedisModuleIO) -> Result { - unsafe { load(rdb, |rdb| RedisModule_LoadSigned.unwrap()(rdb)) } + load(rdb, |rdb| redis_call!(RedisModule_LoadSigned(rdb))) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn load_string(rdb: *mut RedisModuleIO) -> Result { - let p = unsafe { load(rdb, |rdb| RedisModule_LoadString.unwrap()(rdb))? }; + let p = load(rdb, |rdb| redis_call!(RedisModule_LoadString(rdb)))?; Ok(RedisString::from_redis_module_string(ptr::null_mut(), p)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn load_string_buffer(rdb: *mut RedisModuleIO) -> Result { - unsafe { - let mut len = 0; - let buffer = load(rdb, |rdb| { - RedisModule_LoadStringBuffer.unwrap()(rdb, &mut len) - })?; - Ok(RedisBuffer::new(buffer, len)) - } + let mut len = 0; + let buffer = load(rdb, |rdb| { + redis_call!(RedisModule_LoadStringBuffer(rdb, &mut len)) + })?; + Ok(RedisBuffer::new(buffer, len)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -717,68 +762,69 @@ pub fn replicate<'a, T: Into>>( let cmd = CString::new(command).unwrap(); - unsafe { - RedisModule_Replicate.unwrap()( - ctx, - cmd.as_ptr(), - FMT, - final_args.as_ptr(), - final_args.len(), - ) - .into() - } + redis_call!(RedisModule_Replicate( + ctx, + cmd.as_ptr(), + FMT, + final_args.as_ptr(), + final_args.len() + )) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn load_double(rdb: *mut RedisModuleIO) -> Result { - unsafe { load(rdb, |rdb| RedisModule_LoadDouble.unwrap()(rdb)) } + load(rdb, |rdb| redis_call!(RedisModule_LoadDouble(rdb))) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn load_float(rdb: *mut RedisModuleIO) -> Result { - unsafe { load(rdb, |rdb| RedisModule_LoadFloat.unwrap()(rdb)) } + load(rdb, |rdb| redis_call!(RedisModule_LoadFloat(rdb))) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn save_string(rdb: *mut RedisModuleIO, buf: &str) { - unsafe { RedisModule_SaveStringBuffer.unwrap()(rdb, buf.as_ptr().cast::(), buf.len()) }; + redis_call!(raw RedisModule_SaveStringBuffer( + rdb, + buf.as_ptr().cast::(), + buf.len() + )); } #[allow(clippy::not_unsafe_ptr_arg_deref)] /// Save the `RedisString` into the RDB pub fn save_redis_string(rdb: *mut RedisModuleIO, s: &RedisString) { - unsafe { RedisModule_SaveString.unwrap()(rdb, s.inner) }; + redis_call!(raw RedisModule_SaveString(rdb, s.inner)); } #[allow(clippy::not_unsafe_ptr_arg_deref)] /// Save the `&[u8]` into the RDB pub fn save_slice(rdb: *mut RedisModuleIO, buf: &[u8]) { - unsafe { RedisModule_SaveStringBuffer.unwrap()(rdb, buf.as_ptr().cast::(), buf.len()) }; + redis_call!(raw RedisModule_SaveStringBuffer(rdb, buf.as_ptr().cast::(), buf.len())); } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn save_double(rdb: *mut RedisModuleIO, val: f64) { - unsafe { RedisModule_SaveDouble.unwrap()(rdb, val) }; + redis_call!(raw RedisModule_SaveDouble(rdb, val)); } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn save_signed(rdb: *mut RedisModuleIO, val: i64) { - unsafe { RedisModule_SaveSigned.unwrap()(rdb, val) }; + redis_call!(raw RedisModule_SaveSigned(rdb, val)); } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn save_float(rdb: *mut RedisModuleIO, val: f32) { - unsafe { RedisModule_SaveFloat.unwrap()(rdb, val) }; + redis_call!(raw RedisModule_SaveFloat(rdb, val)); } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn save_unsigned(rdb: *mut RedisModuleIO, val: u64) { - unsafe { RedisModule_SaveUnsigned.unwrap()(rdb, val) }; + redis_call!(raw RedisModule_SaveUnsigned(rdb, val)); } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn string_compare(a: *mut RedisModuleString, b: *mut RedisModuleString) -> Ordering { - unsafe { RedisModule_StringCompare.unwrap()(a, b).cmp(&0) } + redis_call!(raw RedisModule_StringCompare(a, b)).cmp(&0) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -787,10 +833,12 @@ pub fn string_append_buffer( s: *mut RedisModuleString, buff: &str, ) -> Status { - unsafe { - RedisModule_StringAppendBuffer.unwrap()(ctx, s, buff.as_ptr().cast::(), buff.len()) - .into() - } + redis_call!(RedisModule_StringAppendBuffer( + ctx, + s, + buff.as_ptr().cast::(), + buff.len() + )) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -799,19 +847,19 @@ pub fn subscribe_to_server_event( event: RedisModuleEvent, callback: RedisModuleEventCallback, ) -> Status { - unsafe { RedisModule_SubscribeToServerEvent.unwrap()(ctx, event, callback).into() } + redis_call!(RedisModule_SubscribeToServerEvent(ctx, event, callback)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn register_info_function(ctx: *mut RedisModuleCtx, callback: RedisModuleInfoFunc) -> Status { - unsafe { RedisModule_RegisterInfoFunc.unwrap()(ctx, callback).into() } + redis_call!(RedisModule_RegisterInfoFunc(ctx, callback)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn add_info_section(ctx: *mut RedisModuleInfoCtx, name: Option<&str>) -> Status { name.map(|n| CString::new(n).unwrap()).map_or_else( - || unsafe { RedisModule_InfoAddSection.unwrap()(ctx, ptr::null_mut()).into() }, - |n| unsafe { RedisModule_InfoAddSection.unwrap()(ctx, n.as_ptr()).into() }, + || redis_call!(RedisModule_InfoAddSection(ctx, ptr::null_mut())), + |n| redis_call!(RedisModule_InfoAddSection(ctx, n.as_ptr())), ) } @@ -819,7 +867,11 @@ pub fn add_info_section(ctx: *mut RedisModuleInfoCtx, name: Option<&str>) -> Sta pub fn add_info_field_str(ctx: *mut RedisModuleInfoCtx, name: &str, content: &str) -> Status { let name = CString::new(name).unwrap(); let content = RedisString::create(None, content); - unsafe { RedisModule_InfoAddFieldString.unwrap()(ctx, name.as_ptr(), content.inner).into() } + redis_call!(RedisModule_InfoAddFieldString( + ctx, + name.as_ptr(), + content.inner + )) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -829,7 +881,7 @@ pub fn add_info_field_long_long( value: c_longlong, ) -> Status { let name = CString::new(name).unwrap(); - unsafe { RedisModule_InfoAddFieldLongLong.unwrap()(ctx, name.as_ptr(), value).into() } + redis_call!(RedisModule_InfoAddFieldLongLong(ctx, name.as_ptr(), value)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] @@ -839,40 +891,36 @@ pub fn add_info_field_unsigned_long_long( value: c_ulonglong, ) -> Status { let name = CString::new(name).unwrap(); - unsafe { RedisModule_InfoAddFieldULongLong.unwrap()(ctx, name.as_ptr(), value).into() } + redis_call!(RedisModule_InfoAddFieldULongLong(ctx, name.as_ptr(), value)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn add_info_field_double(ctx: *mut RedisModuleInfoCtx, name: &str, value: c_double) -> Status { let name = CString::new(name).unwrap(); - unsafe { RedisModule_InfoAddFieldDouble.unwrap()(ctx, name.as_ptr(), value).into() } + redis_call!(RedisModule_InfoAddFieldDouble(ctx, name.as_ptr(), value)) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn add_info_begin_dict_field(ctx: *mut RedisModuleInfoCtx, name: &str) -> Status { let name = CString::new(name).unwrap(); - unsafe { RedisModule_InfoBeginDictField.unwrap()(ctx, name.as_ptr()).into() } + redis_call!(RedisModule_InfoBeginDictField(ctx, name.as_ptr())) } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn add_info_end_dict_field(ctx: *mut RedisModuleInfoCtx) -> Status { - unsafe { RedisModule_InfoEndDictField.unwrap()(ctx).into() } + redis_call!(RedisModule_InfoEndDictField(ctx)) } -/// # Safety -/// -/// This function is safe to use as it doesn't perform any work with -/// the [RedisModuleCtx] pointer except for passing it to the redis server. -/// /// # Panics /// /// Panics when the [RedisModule_ExportSharedAPI] is unavailable. -pub unsafe fn export_shared_api( +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn export_shared_api( ctx: *mut RedisModuleCtx, func: *const ::std::os::raw::c_void, name: *const ::std::os::raw::c_char, ) { - RedisModule_ExportSharedAPI.unwrap()(ctx, name, func as *mut ::std::os::raw::c_void); + redis_call!(raw RedisModule_ExportSharedAPI(ctx, name, func as *mut _)); } /// # Safety @@ -890,19 +938,135 @@ pub unsafe fn notify_keyspace_event( keyname: &RedisString, ) -> Status { let event = CString::new(event).unwrap(); - RedisModule_NotifyKeyspaceEvent.unwrap()(ctx, event_type.bits(), event.as_ptr(), keyname.inner) - .into() + redis_call!(RedisModule_NotifyKeyspaceEvent( + ctx, + event_type.bits(), + event.as_ptr(), + keyname.inner + )) } /// # Panics /// /// Panics when the [RedisModule_GetNotifyKeyspaceEvents] is unavailable. -#[must_use] pub fn get_keyspace_events() -> NotifyEvent { - unsafe { - let events = RedisModule_GetNotifyKeyspaceEvents.unwrap()(); - NotifyEvent::from_bits_truncate(events) - } + let events = redis_call!(RedisModule_GetNotifyKeyspaceEvents()); + NotifyEvent::from_bits_truncate(events) +} + +/// Starts the stream iterator. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn stream_iterator_start( + key: *mut RedisModuleKey, + flags: ::std::os::raw::c_int, + start_id: *mut RedisModuleStreamID, + end_id: *mut RedisModuleStreamID, +) -> Status { + redis_call!(RedisModule_StreamIteratorStart( + key, flags, start_id, end_id + )) +} + +/// Modifies the id passed to point to the next item within the stream. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn stream_iterator_next_id( + key: *mut RedisModuleKey, + id: *mut RedisModuleStreamID, + fields_count: &mut ::std::os::raw::c_long, +) -> Status { + redis_call!(RedisModule_StreamIteratorNextID(key, id, fields_count)) +} + +/// Obtains a pointer to the key -> value pair within the stream using +/// the iterator. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn stream_iterator_next_field( + key: *mut RedisModuleKey, + field_ptr: *mut *mut RedisModuleString, + value_ptr: *mut *mut RedisModuleString, +) -> Status { + redis_call!(RedisModule_StreamIteratorNextField( + key, field_ptr, value_ptr + )) +} + +/// Deletes the stream iterator accessible by key. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn stream_iterator_delete(key: *mut RedisModuleKey) -> Status { + redis_call!(RedisModule_StreamIteratorDelete(key)) +} + +/// Returns the timer information. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn get_timer_info( + ctx: *mut RedisModuleCtx, + id: RedisModuleTimerID, + remaining: *mut u64, + data: *mut *mut ::std::os::raw::c_void, +) -> Status { + redis_call!(RedisModule_GetTimerInfo(ctx, id, remaining, data)) +} + +/// Stops the timer with the provided id. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn stop_timer( + ctx: *mut RedisModuleCtx, + id: RedisModuleTimerID, + data: *mut *mut ::std::os::raw::c_void, +) -> Status { + redis_call!(RedisModule_StopTimer(ctx, id, data)) +} + +/// Reports the "wrong arity" redis error. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn wrong_arity(ctx: *mut RedisModuleCtx) -> Status { + redis_call!(RedisModule_WrongArity(ctx)) +} + +/// Checks the passed ACL permissions. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn acl_check_key_permissions( + user: *mut RedisModuleUser, + key: *mut RedisModuleString, + flags: ::std::os::raw::c_int, +) -> Status { + redis_call!(RedisModule_ACLCheckKeyPermissions(user, key, flags)) +} + +/// Adds a post notification job +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn add_post_notification_job( + ctx: *mut RedisModuleCtx, + callback: RedisModulePostNotificationJobFunc, + pd: *mut ::std::os::raw::c_void, + free_pd: ::std::option::Option, +) -> Status { + redis_call!(RedisModule_AddPostNotificationJob( + ctx, callback, pd, free_pd + )) +} + +/// Returns the module user object for the user name passed. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn get_module_user_from_user_name(name: *mut RedisModuleString) -> *mut RedisModuleUser { + redis_call!(RedisModule_GetModuleUserFromUserName(name)) +} + +/// Returns the key type. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn key_type(key: *mut RedisModuleKey) -> KeyType { + redis_call!(RedisModule_KeyType(key)) +} + +/// If the key is open for writing, set the specified module type object +/// as the value of the key, deleting the old value if any. +#[allow(clippy::not_unsafe_ptr_arg_deref)] +pub fn module_type_set_value( + key: *mut RedisModuleKey, + mt: *mut RedisModuleType, + value: *mut ::std::os::raw::c_void, +) -> Status { + redis_call!(RedisModule_ModuleTypeSetValue(key, mt, value)) } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -925,5 +1089,5 @@ impl From for Version { #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn is_io_error(rdb: *mut RedisModuleIO) -> bool { - unsafe { RedisModule_IsIOError.unwrap()(rdb) != 0 } + redis_call!(raw RedisModule_IsIOError(rdb)) != 0 } diff --git a/src/stream.rs b/src/stream.rs index 4f37c2a5..4e1152f6 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -37,15 +37,13 @@ impl<'key> StreamIterator<'key> { 0 }; - let res = unsafe { - raw::RedisModule_StreamIteratorStart.unwrap()( - key.key_inner, - flags, - from.as_mut().map_or(ptr::null_mut(), |v| v), - to.as_mut().map_or(ptr::null_mut(), |v| v), - ) - }; - if Status::Ok == res.into() { + let res = raw::stream_iterator_start( + key.key_inner, + flags, + from.as_mut().map_or(ptr::null_mut(), |v| v), + to.as_mut().map_or(ptr::null_mut(), |v| v), + ); + if Status::Ok == res { Ok(StreamIterator { key }) } else { Err(RedisError::Str("Failed creating stream iterator")) @@ -61,28 +59,13 @@ impl<'key> Iterator for StreamIterator<'key> { let mut num_fields: c_long = 0; let mut field_name: *mut raw::RedisModuleString = ptr::null_mut(); let mut field_val: *mut raw::RedisModuleString = ptr::null_mut(); - if Status::Ok - != unsafe { - raw::RedisModule_StreamIteratorNextID.unwrap()( - self.key.key_inner, - &mut id, - &mut num_fields, - ) - } - .into() + if Status::Ok != raw::stream_iterator_next_id(self.key.key_inner, &mut id, &mut num_fields) { return None; } let mut fields = Vec::new(); while Status::Ok - == unsafe { - raw::RedisModule_StreamIteratorNextField.unwrap()( - self.key.key_inner, - &mut field_name, - &mut field_val, - ) - .into() - } + == raw::stream_iterator_next_field(self.key.key_inner, &mut field_name, &mut field_val) { fields.push(( RedisString::from_redis_module_string(ptr::null_mut(), field_name), @@ -95,6 +78,6 @@ impl<'key> Iterator for StreamIterator<'key> { impl<'key> Drop for StreamIterator<'key> { fn drop(&mut self) { - unsafe { raw::RedisModule_StreamIteratorDelete.unwrap()(self.key.key_inner) }; + raw::stream_iterator_delete(self.key.key_inner); } } From ad76a536953c96fe3a13b1e83b7921d2f01ef3e2 Mon Sep 17 00:00:00 2001 From: Victor Polevoy Date: Wed, 22 Nov 2023 15:36:45 +0100 Subject: [PATCH 2/4] Update dependencies --- Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7dd39967..ca7e0b3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -126,7 +126,7 @@ strum_macros = "0.25" backtrace = "0.3" linkme = "0.3" serde = { version = "1", features = ["derive"] } -nix = "0.26" +nix = "0.27" cfg-if = "1" redis-module-macros-internals = { path = "./redismodule-rs-macros-internals" } redis-module-macros = { path = "./redismodule-rs-macros"} @@ -140,7 +140,7 @@ redis-module-macros = { path = "./redismodule-rs-macros"} redis-module = { path = "./", default-features = false, features = ["min-redis-compatibility-version-7-2"] } [build-dependencies] -bindgen = "0.68" +bindgen = "0.69" cc = "1" [features] From e18e9822f0cda0c1f5484ebf045d86fabb6b05a1 Mon Sep 17 00:00:00 2001 From: Victor Polevoy Date: Wed, 22 Nov 2023 15:41:31 +0100 Subject: [PATCH 3/4] Remove the unused macro --- src/raw.rs | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/raw.rs b/src/raw.rs index 8e5da690..591ff48d 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -316,16 +316,6 @@ pub fn call_reply_promise_abort( redis_call!(RedisModule_CallReplyPromiseAbort(reply, private_data)) } -macro_rules! generate_transparent_binding { - ($(#[$outer:meta])* $vis:vis $rust_name:ident => $raw_function:ident, $ret_type:ty, $(($arg:ident, $typ:ty)),*) => { - $(#[$outer])* - #[allow(clippy::not_unsafe_ptr_arg_deref)] - $vis fn $rust_name($($arg: $typ),*) -> $ret_type { - raw_call!($raw_function, $($arg),*) - } - }; -} - #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn call_reply_array_element( reply: *mut RedisModuleCallReply, @@ -334,16 +324,6 @@ pub fn call_reply_array_element( redis_call!(RedisModule_CallReplyArrayElement(reply, idx)) } -// generate_transparent_binding!( -// /// # Panics -// /// -// /// Panics if the Redis server doesn't support replying with bool (since RESP3). -// pub call_reply_set_element => RedisModule_CallReplySetElement, -// *mut RedisModuleCallReply, -// (reply, *mut RedisModuleCallReply), -// (idx, usize) -// ); - /// # Panics /// /// Panics if the Redis server doesn't support replying with bool (since RESP3). From 68a0705b7f3d95a893eda19390e4dff51d3a4263 Mon Sep 17 00:00:00 2001 From: Victor Polevoy Date: Fri, 24 Nov 2023 10:18:29 +0100 Subject: [PATCH 4/4] Hide the "raw" module. Also extracts the only public enum which is used: Status. Hiding the "raw" module helps to hide the implementation detail and avoid often API compatibility issues when a change is needed. --- redismodule-rs-macros-internals/Cargo.toml | 2 +- src/context/call_reply.rs | 2 +- src/context/commands.rs | 8 +-- src/context/mod.rs | 76 ++++++++++++---------- src/context/thread_safe.rs | 4 +- src/context/timer.rs | 6 +- src/key.rs | 21 +++--- src/lib.rs | 4 +- src/macros.rs | 32 ++++----- src/raw.rs | 58 +---------------- src/redismodule.rs | 12 ++-- src/status.rs | 28 ++++++++ 12 files changed, 119 insertions(+), 134 deletions(-) create mode 100644 src/status.rs diff --git a/redismodule-rs-macros-internals/Cargo.toml b/redismodule-rs-macros-internals/Cargo.toml index 7e175e57..42eaa42a 100644 --- a/redismodule-rs-macros-internals/Cargo.toml +++ b/redismodule-rs-macros-internals/Cargo.toml @@ -12,7 +12,7 @@ categories = ["database", "api-bindings"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -syn = { version="1", features = ["full", "extra-traits"]} +syn = { version = "1", features = ["full", "extra-traits"]} quote = "1" lazy_static = "1" proc-macro2 = "1" diff --git a/src/context/call_reply.rs b/src/context/call_reply.rs index 9000ba1f..426b5dd3 100644 --- a/src/context/call_reply.rs +++ b/src/context/call_reply.rs @@ -9,7 +9,7 @@ use std::{ use libc::c_void; -use crate::{deallocate_pointer, raw::*, Context, RedisError, RedisLockIndicator}; +use crate::{deallocate_pointer, raw::*, Context, RedisError, RedisLockIndicator, Status}; pub struct StringCallReply<'root> { reply: NonNull, diff --git a/src/context/commands.rs b/src/context/commands.rs index dfc80ef5..156a5663 100644 --- a/src/context/commands.rs +++ b/src/context/commands.rs @@ -391,7 +391,7 @@ api! {[ 0, 0, ) - } == raw::Status::Err as i32 + } == Status::Err as i32 { return Err(RedisError::String(format!( "Failed register command {}.", @@ -444,7 +444,7 @@ api! {[ args: ptr::null_mut(), }; - if unsafe { RedisModule_SetCommandInfo(command, &mut redis_command_info as *mut raw::RedisModuleCommandInfo) } == raw::Status::Err as i32 { + if unsafe { RedisModule_SetCommandInfo(command, &mut redis_command_info as *mut raw::RedisModuleCommandInfo) } == Status::Err as i32 { return Err(RedisError::String(format!( "Failed setting info for command {}.", command_info.name @@ -454,12 +454,12 @@ api! {[ // the only CString pointers which are not freed are those of the key_specs, lets free them here. key_specs.into_iter().for_each(|v|{ if !v.notes.is_null() { - unsafe{ drop(CString::from_raw(v.notes as *mut c_char)) }; + drop(unsafe { CString::from_raw(v.notes as *mut c_char) }); } if v.begin_search_type == raw::RedisModuleKeySpecBeginSearchType_REDISMODULE_KSPEC_BS_KEYWORD { let keyword = unsafe{v.bs.keyword.keyword}; if !keyword.is_null() { - unsafe{ drop(CString::from_raw(v.bs.keyword.keyword as *mut c_char)) }; + drop(unsafe { CString::from_raw(v.bs.keyword.keyword as *mut c_char) }); } } }); diff --git a/src/context/mod.rs b/src/context/mod.rs index 5d7e4eba..e8df2fb8 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -469,17 +469,17 @@ impl Context { } #[allow(clippy::must_use_candidate)] - pub fn reply_simple_string(&self, s: &str) -> raw::Status { + pub fn reply_simple_string(&self, s: &str) -> Status { let msg = Self::str_as_legal_resp_string(s); raw::reply_with_simple_string(self.ctx, msg.as_ptr()) } #[allow(clippy::must_use_candidate)] - pub fn reply_error_string(&self, s: &str) -> raw::Status { + pub fn reply_error_string(&self, s: &str) -> Status { raw::reply_with_error(self.ctx, s) } - pub fn reply_with_key(&self, result: RedisValueKey) -> raw::Status { + pub fn reply_with_key(&self, result: RedisValueKey) -> Status { match result { RedisValueKey::Integer(i) => raw::reply_with_long_long(self.ctx, i), RedisValueKey::String(s) => { @@ -497,7 +497,7 @@ impl Context { /// /// Will panic if methods used are missing in redismodule.h #[allow(clippy::must_use_candidate)] - pub fn reply(&self, result: RedisResult) -> raw::Status { + pub fn reply(&self, result: RedisResult) -> Status { match result { Ok(RedisValue::Bool(v)) => raw::reply_with_bool(self.ctx, v.into()), Ok(RedisValue::Integer(v)) => raw::reply_with_long_long(self.ctx, v), @@ -540,7 +540,7 @@ impl Context { self.reply(Ok(elem)); } - raw::Status::Ok + Status::Ok } Ok(RedisValue::Map(map)) => { @@ -551,7 +551,7 @@ impl Context { self.reply(Ok(value)); } - raw::Status::Ok + Status::Ok } Ok(RedisValue::OrderedMap(map)) => { @@ -562,7 +562,7 @@ impl Context { self.reply(Ok(value)); } - raw::Status::Ok + Status::Ok } Ok(RedisValue::Set(set)) => { @@ -571,7 +571,7 @@ impl Context { self.reply_with_key(e); }); - raw::Status::Ok + Status::Ok } Ok(RedisValue::OrderedSet(set)) => { @@ -580,19 +580,19 @@ impl Context { self.reply_with_key(e); }); - raw::Status::Ok + Status::Ok } Ok(RedisValue::Null) => raw::reply_with_null(self.ctx), - Ok(RedisValue::NoReply) => raw::Status::Ok, + Ok(RedisValue::NoReply) => Status::Ok, Ok(RedisValue::StaticError(s)) => self.reply_error_string(s), Err(RedisError::WrongArity) => { if self.is_keys_position_request() { // We can't return a result since we don't have a client - raw::Status::Err + Status::Err } else { raw::wrong_arity(self.ctx) } @@ -671,7 +671,7 @@ impl Context { event_type: raw::NotifyEvent, event: &str, keyname: &RedisString, - ) -> raw::Status { + ) -> Status { unsafe { raw::notify_keyspace_event(self.ctx, event_type, event, keyname) } } @@ -791,27 +791,37 @@ impl Context { acl_permission_result.map_err(|_e| RedisError::Str("User does not have permissions on key")) } - /// When running inside a key space notification callback, it is dangerous and highly discouraged to perform any write - /// operation. In order to still perform write actions in this scenario, Redis provides this API ([add_post_notification_job]) - /// that allows to register a job callback which Redis will call when the following condition holds: - /// - /// 1. It is safe to perform any write operation. - /// 2. The job will be called atomically along side the key space notification. - /// - /// Notice, one job might trigger key space notifications that will trigger more jobs. - /// This raises a concerns of entering an infinite loops, we consider infinite loops - /// as a logical bug that need to be fixed in the module, an attempt to protect against - /// infinite loops by halting the execution could result in violation of the feature correctness - /// and so Redis will make no attempt to protect the module from infinite loops. - pub fn add_post_notification_job(&self, callback: F) -> Status { - let callback = Box::into_raw(Box::new(Some(callback))); - raw::add_post_notification_job( - self.ctx, - Some(post_notification_job::), - callback as *mut c_void, - Some(post_notification_job_free_callback::), - ) - } + api!( + [RedisModule_AddPostNotificationJob], + /// When running inside a key space notification callback, it is dangerous and highly discouraged to perform any write + /// operation. In order to still perform write actions in this scenario, Redis provides this API ([add_post_notification_job]) + /// that allows to register a job callback which Redis will call when the following condition holds: + /// + /// 1. It is safe to perform any write operation. + /// 2. The job will be called atomically along side the key space notification. + /// + /// Notice, one job might trigger key space notifications that will trigger more jobs. + /// This raises a concerns of entering an infinite loops, we consider infinite loops + /// as a logical bug that need to be fixed in the module, an attempt to protect against + /// infinite loops by halting the execution could result in violation of the feature correctness + /// and so Redis will make no attempt to protect the module from infinite loops. + pub fn add_post_notification_job( + &self, + callback: F, + ) -> Status { + let callback = Box::into_raw(Box::new(Some(callback))); + unsafe { + RedisModule_AddPostNotificationJob( + self.ctx, + Some(post_notification_job::), + callback as *mut c_void, + Some(post_notification_job_free_callback::), + ) + } + .try_into() + .unwrap() + } + ); api!( [RedisModule_AvoidReplicaTraffic], diff --git a/src/context/thread_safe.rs b/src/context/thread_safe.rs index 2017d2fb..1490665c 100644 --- a/src/context/thread_safe.rs +++ b/src/context/thread_safe.rs @@ -4,7 +4,7 @@ use std::ops::{Deref, DerefMut}; use std::ptr; use crate::context::blocked::BlockedClient; -use crate::{raw, Context, RedisResult}; +use crate::{raw, Context, RedisResult, Status}; pub struct RedisGILGuardScope<'ctx, 'mutex, T, G: RedisLockIndicator> { _context: &'ctx G, @@ -158,7 +158,7 @@ impl ThreadSafeContext { /// The Redis modules API does not require locking for `Reply` functions, /// so we pass through its functionality directly. #[allow(clippy::must_use_candidate)] - pub fn reply(&self, r: RedisResult) -> raw::Status { + pub fn reply(&self, r: RedisResult) -> Status { let ctx = Context::new(self.ctx); ctx.reply(r) } diff --git a/src/context/timer.rs b/src/context/timer.rs index 96e1199d..52f8902c 100644 --- a/src/context/timer.rs +++ b/src/context/timer.rs @@ -2,8 +2,8 @@ use std::convert::TryInto; use std::ffi::c_void; use std::time::Duration; -use crate::raw; use crate::raw::RedisModuleTimerID; +use crate::{raw, Status}; use crate::{Context, RedisError}; // We use `repr(C)` since we access the underlying data field directly. @@ -62,7 +62,7 @@ impl Context { let status = raw::stop_timer(self.ctx, timer_id, &mut data); - if status != raw::Status::Ok { + if status != Status::Ok { return Err(RedisError::Str( "RedisModule_StopTimer failed, timer may not exist", )); @@ -87,7 +87,7 @@ impl Context { let status = raw::get_timer_info(self.ctx, timer_id, &mut remaining, &mut data); - if status != raw::Status::Ok { + if status != Status::Ok { return Err(RedisError::Str( "RedisModule_GetTimerInfo failed, timer may not exist", )); diff --git a/src/key.rs b/src/key.rs index 555afd47..5292da62 100644 --- a/src/key.rs +++ b/src/key.rs @@ -19,6 +19,7 @@ use crate::stream::StreamIterator; use crate::RedisError; use crate::RedisResult; use crate::RedisString; +use crate::Status; use bitflags::bitflags; /// `RedisKey` is an abstraction over a Redis key that allows readonly @@ -240,12 +241,12 @@ impl RedisKeyWritable { } #[allow(clippy::must_use_candidate)] - pub fn hash_set(&self, field: &str, value: RedisString) -> raw::Status { + pub fn hash_set(&self, field: &str, value: RedisString) -> Status { raw::hash_set(self.key_inner, field, value.inner) } #[allow(clippy::must_use_candidate)] - pub fn hash_del(&self, field: &str) -> raw::Status { + pub fn hash_del(&self, field: &str) -> Status { raw::hash_del(self.key_inner, field) } @@ -273,13 +274,13 @@ impl RedisKeyWritable { // `list_push_head` inserts the specified element at the head of the list stored at this key. #[allow(clippy::must_use_candidate)] - pub fn list_push_head(&self, element: RedisString) -> raw::Status { + pub fn list_push_head(&self, element: RedisString) -> Status { raw::list_push(self.key_inner, raw::Where::ListHead, element.inner) } // `list_push_tail` inserts the specified element at the tail of the list stored at this key. #[allow(clippy::must_use_candidate)] - pub fn list_push_tail(&self, element: RedisString) -> raw::Status { + pub fn list_push_tail(&self, element: RedisString) -> Status { raw::list_push(self.key_inner, raw::Where::ListTail, element.inner) } @@ -321,19 +322,19 @@ impl RedisKeyWritable { })?; match raw::set_expire(self.key_inner, exp_time) { - raw::Status::Ok => REDIS_OK, + Status::Ok => REDIS_OK, // Error may occur if the key wasn't open for writing or is an // empty key. - raw::Status::Err => Err(RedisError::Str("Error while setting key expire")), + Status::Err => Err(RedisError::Str("Error while setting key expire")), } } pub fn write(&self, val: &str) -> RedisResult { let val_str = RedisString::create(NonNull::new(self.ctx), val); 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")), + Status::Ok => REDIS_OK, + Status::Err => Err(RedisError::Str("Error while setting key")), } } @@ -569,7 +570,7 @@ impl<'a> StringDMA<'a> { pub fn write(&mut self, data: &[u8]) -> Result<&mut Self, RedisError> { if self.buffer.len() != data.len() { - if raw::Status::Ok == raw::string_truncate(self.key.key_inner, data.len()) { + if Status::Ok == raw::string_truncate(self.key.key_inner, data.len()) { let mut length: size_t = 0; let dma = raw::string_dma(self.key.key_inner, &mut length, raw::KeyMode::WRITE); self.buffer = unsafe { std::slice::from_raw_parts_mut(dma.cast::(), length) }; @@ -584,7 +585,7 @@ impl<'a> StringDMA<'a> { pub fn append(&mut self, data: &[u8]) -> Result<&mut Self, RedisError> { let current_len = self.buffer.len(); let new_len = current_len + data.len(); - if raw::Status::Ok == raw::string_truncate(self.key.key_inner, new_len) { + if Status::Ok == raw::string_truncate(self.key.key_inner, new_len) { let mut length: size_t = 0; let dma = raw::string_dma(self.key.key_inner, &mut length, raw::KeyMode::WRITE); self.buffer = unsafe { std::slice::from_raw_parts_mut(dma.cast::(), length) }; diff --git a/src/lib.rs b/src/lib.rs index 32ec8e1a..0a651f4d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,11 +46,13 @@ pub use crate::context::{ InfoContextBuilderFieldBottomLevelValue, InfoContextBuilderFieldTopLevelValue, InfoContextFieldBottomLevelData, InfoContextFieldTopLevelData, OneInfoSectionData, }; -pub use crate::raw::*; +mod status; +pub(crate) use crate::raw::*; pub use crate::redismodule::*; use backtrace::Backtrace; use context::server_events::INFO_COMMAND_HANDLER_LIST; pub use redis_module_macros::*; +pub use status::Status; /// The detached Redis module context (the context of this module). It /// is only set to a proper value after the module is initialised via the diff --git a/src/macros.rs b/src/macros.rs index 43b4f21b..2cea4b44 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -34,9 +34,9 @@ macro_rules! redis_command { $lastkey, $keystep, ) - } == $crate::raw::Status::Err as c_int + } == $crate::Status::Err as c_int { - return $crate::raw::Status::Err as c_int; + return $crate::Status::Err as c_int; } }}; } @@ -65,7 +65,7 @@ macro_rules! redis_event_handler { redis_key, ); - $crate::raw::Status::Ok as c_int + $crate::Status::Ok as c_int } if unsafe { @@ -74,9 +74,9 @@ macro_rules! redis_event_handler { $event_type.bits(), Some(__handle_event), ) - } == $crate::raw::Status::Err as c_int + } == $crate::Status::Err as c_int { - return $crate::raw::Status::Err as c_int; + return $crate::Status::Err as c_int; } }}; } @@ -217,7 +217,7 @@ macro_rules! redis_module { name_buffer.as_ptr().cast::(), module_version, raw::REDISMODULE_APIVER_1 as c_int, - ) } == raw::Status::Err as c_int { return raw::Status::Err as c_int; } + ) } == $crate::Status::Err as c_int { return $crate::Status::Err as c_int; } let context = $crate::Context::new(ctx); unsafe { @@ -227,7 +227,7 @@ macro_rules! redis_module { $( if (&$data_type).create_data_type(ctx).is_err() { - return raw::Status::Err as c_int; + return $crate::Status::Err as c_int; } )* @@ -235,8 +235,8 @@ macro_rules! redis_module { $crate::redis_command!(ctx, $name, $command, $flags, $firstkey, $lastkey, $keystep); )* - if $crate::commands::register_commands(&context) == raw::Status::Err { - return raw::Status::Err as c_int; + if $crate::commands::register_commands(&context) == $crate::Status::Err { + return $crate::Status::Err as c_int; } $( @@ -253,7 +253,7 @@ macro_rules! redis_module { Ok(v) => v, Err(e) => { context.log_warning(&format!("{e}")); - return raw::Status::Err as c_int; + return $crate::Status::Err as c_int; } } } else { @@ -269,7 +269,7 @@ macro_rules! redis_module { Ok(v) => v, Err(e) => { context.log_warning(&format!("{e}")); - return raw::Status::Err as c_int; + return $crate::Status::Err as c_int; } } } else { @@ -285,7 +285,7 @@ macro_rules! redis_module { Ok(v) => v, Err(e) => { context.log_warning(&format!("{e}")); - return raw::Status::Err as c_int; + return $crate::Status::Err as c_int; } } } else { @@ -301,7 +301,7 @@ macro_rules! redis_module { Ok(v) => v, Err(e) => { context.log_warning(&format!("{e}")); - return raw::Status::Err as c_int; + return $crate::Status::Err as c_int; } } } else { @@ -329,7 +329,7 @@ macro_rules! redis_module { if let Err(e) = register_server_events(&context) { context.log_warning(&format!("{e}")); - return raw::Status::Err as c_int; + return $crate::Status::Err as c_int; } $( @@ -338,7 +338,7 @@ macro_rules! redis_module { } )* - raw::Status::Ok as c_int + $crate::Status::Ok as c_int } #[no_mangle] @@ -355,7 +355,7 @@ macro_rules! redis_module { } )* - $crate::raw::Status::Ok as c_int + $crate::Status::Ok as c_int } } } diff --git a/src/raw.rs b/src/raw.rs index 591ff48d..132d523d 100644 --- a/src/raw.rs +++ b/src/raw.rs @@ -1,28 +1,16 @@ -// Allow dead code in here in case I want to publish it as a crate at some -// point. -#![allow(dead_code)] - -extern crate enum_primitive_derive; -extern crate libc; -extern crate num_traits; - use std::cmp::Ordering; use std::ffi::{c_ulonglong, CString}; use std::os::raw::{c_char, c_double, c_int, c_long, c_longlong, c_void}; use std::ptr; use std::slice; -use crate::RedisResult; use bitflags::bitflags; use enum_primitive_derive::Primitive; use libc::size_t; use crate::error::Error; pub use crate::redisraw::bindings::*; -use crate::{context::StrCallArgs, RedisString}; -use crate::{RedisBuffer, RedisError}; - -const GENERIC_ERROR_MESSAGE: &str = "Generic error."; +use crate::{context::StrCallArgs, RedisBuffer, RedisError, RedisString, Status}; bitflags! { pub struct KeyMode: c_int { @@ -130,30 +118,6 @@ pub enum Aux { After = REDISMODULE_AUX_AFTER_RDB, } -#[derive(Primitive, Debug, PartialEq, Eq)] -pub enum Status { - Ok = REDISMODULE_OK, - Err = REDISMODULE_ERR, -} - -impl From for RedisResult<()> { - fn from(value: Status) -> Self { - match value { - Status::Ok => Ok(()), - Status::Err => Err(RedisError::Str(GENERIC_ERROR_MESSAGE)), - } - } -} - -impl From for Result<(), &str> { - fn from(s: Status) -> Self { - match s { - Status::Ok => Ok(()), - Status::Err => Err(GENERIC_ERROR_MESSAGE), - } - } -} - bitflags! { #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)] pub struct NotifyEvent : c_int { @@ -192,26 +156,6 @@ pub enum CommandFlag { Movablekeys, } -const fn command_flag_repr(flag: &CommandFlag) -> &'static str { - use crate::raw::CommandFlag::*; - match flag { - Write => "write", - Readonly => "readonly", - Denyoom => "denyoom", - Admin => "admin", - Pubsub => "pubsub", - Noscript => "noscript", - Random => "random", - SortForScript => "sort_for_script", - Loading => "loading", - Stale => "stale", - SkipMonitor => "skip_monitor", - Asking => "asking", - Fast => "fast", - Movablekeys => "movablekeys", - } -} - // This is the one static function we need to initialize a module. // bindgen does not generate it for us (probably since it's defined as static in redismodule.h). #[allow(improper_ctypes)] diff --git a/src/redismodule.rs b/src/redismodule.rs index 46ed3ec1..1f3db084 100644 --- a/src/redismodule.rs +++ b/src/redismodule.rs @@ -16,7 +16,7 @@ use serde::de::{Error, SeqAccess}; pub use crate::raw; pub use crate::rediserror::RedisError; pub use crate::redisvalue::RedisValue; -use crate::Context; +use crate::{Context, Status}; /// A short-hand type that stores a [std::result::Result] with custom /// type and [RedisError]. @@ -183,7 +183,7 @@ impl RedisString { str::from_utf8(Self::string_as_slice(ptr)) } - pub fn append(&mut self, s: &str) -> raw::Status { + pub fn append(&mut self, s: &str) -> Status { raw::string_append_buffer(self.ctx, self.inner, s) } @@ -239,16 +239,16 @@ impl RedisString { pub fn parse_integer(&self) -> Result { let mut val: i64 = 0; match raw::string_to_longlong(self.inner, &mut val) { - raw::Status::Ok => Ok(val), - raw::Status::Err => Err(RedisError::Str("Couldn't parse as integer")), + Status::Ok => Ok(val), + Status::Err => Err(RedisError::Str("Couldn't parse as integer")), } } pub fn parse_float(&self) -> Result { let mut val: f64 = 0.0; match raw::string_to_double(self.inner, &mut val) { - raw::Status::Ok => Ok(val), - raw::Status::Err => Err(RedisError::Str("Couldn't parse as float")), + Status::Ok => Ok(val), + Status::Err => Err(RedisError::Str("Couldn't parse as float")), } } diff --git a/src/status.rs b/src/status.rs new file mode 100644 index 00000000..d73eef74 --- /dev/null +++ b/src/status.rs @@ -0,0 +1,28 @@ +use crate::{RedisError, RedisResult}; +use enum_primitive_derive::Primitive; + +const GENERIC_ERROR_MESSAGE: &str = "Generic error."; + +#[derive(Primitive, Debug, PartialEq, Eq)] +pub enum Status { + Ok = crate::raw::REDISMODULE_OK, + Err = crate::raw::REDISMODULE_ERR, +} + +impl From for RedisResult<()> { + fn from(value: Status) -> Self { + match value { + Status::Ok => Ok(()), + Status::Err => Err(RedisError::Str(GENERIC_ERROR_MESSAGE)), + } + } +} + +impl From for Result<(), &str> { + fn from(s: Status) -> Self { + match s { + Status::Ok => Ok(()), + Status::Err => Err(GENERIC_ERROR_MESSAGE), + } + } +}