use std::ffi::CString;
use std::os::raw::{c_char, c_int, c_long};
use std::ptr;
use crate::key::{RedisKey, RedisKeyWritable};
use crate::raw;
use crate::LogLevel;
use crate::{RedisError, RedisResult, RedisString, RedisValue};
#[cfg(feature = "experimental-api")]
mod timer;
#[cfg(feature = "experimental-api")]
pub(crate) mod thread_safe;
#[cfg(feature = "experimental-api")]
pub(crate) mod blocked;
pub struct Context {
pub(crate) ctx: *mut raw::RedisModuleCtx,
}
impl Context {
pub fn new(ctx: *mut raw::RedisModuleCtx) -> Self {
Self { ctx }
}
pub fn dummy() -> Self {
Self {
ctx: ptr::null_mut(),
}
}
pub fn log(&self, level: LogLevel, message: &str) {
crate::logging::log_internal(self.ctx, level, message);
}
pub fn log_debug(&self, message: &str) {
self.log(LogLevel::Debug, message);
}
pub fn log_notice(&self, message: &str) {
self.log(LogLevel::Notice, message);
}
pub fn log_verbose(&self, message: &str) {
self.log(LogLevel::Verbose, message);
}
pub fn log_warning(&self, message: &str) {
self.log(LogLevel::Warning, message);
}
pub fn auto_memory(&self) {
unsafe {
raw::RedisModule_AutoMemory.unwrap()(self.ctx);
}
}
pub fn is_keys_position_request(&self) -> bool {
if cfg!(feature = "test") {
return false;
}
let result = unsafe { raw::RedisModule_IsKeysPositionRequest.unwrap()(self.ctx) };
result != 0
}
pub fn key_at_pos(&self, pos: i32) {
unsafe {
raw::RedisModule_KeyAtPos.unwrap()(self.ctx, pos as c_int);
}
}
pub fn call(&self, command: &str, args: &[&str]) -> RedisResult {
let terminated_args: Vec<RedisString> = 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();
let cmd = CString::new(command).unwrap();
let reply: *mut raw::RedisModuleCallReply = unsafe {
let p_call = raw::RedisModule_Call.unwrap();
p_call(
self.ctx,
cmd.as_ptr(),
raw::FMT,
inner_args.as_ptr() as *mut c_char,
terminated_args.len(),
)
};
let result = Self::parse_call_reply(reply);
if !reply.is_null() {
raw::free_call_reply(reply);
}
result
}
fn parse_call_reply(reply: *mut raw::RedisModuleCallReply) -> RedisResult {
match raw::call_reply_type(reply) {
raw::ReplyType::Error => Err(RedisError::String(raw::call_reply_string(reply))),
raw::ReplyType::Unknown => Err(RedisError::Str("Error on method call")),
raw::ReplyType::Array => {
let length = raw::call_reply_length(reply);
let mut vec = Vec::with_capacity(length);
for i in 0..length {
vec.push(Self::parse_call_reply(raw::call_reply_array_element(
reply, i,
))?)
}
Ok(RedisValue::Array(vec))
}
raw::ReplyType::Integer => Ok(RedisValue::Integer(raw::call_reply_integer(reply))),
raw::ReplyType::String => Ok(RedisValue::SimpleString(raw::call_reply_string(reply))),
raw::ReplyType::Null => Ok(RedisValue::Null),
}
}
pub fn reply(&self, r: RedisResult) -> raw::Status {
match r {
Ok(RedisValue::Integer(v)) => unsafe {
raw::RedisModule_ReplyWithLongLong.unwrap()(self.ctx, v).into()
},
Ok(RedisValue::Float(v)) => unsafe {
raw::RedisModule_ReplyWithDouble.unwrap()(self.ctx, v).into()
},
Ok(RedisValue::SimpleStringStatic(s)) => unsafe {
let msg = CString::new(s).unwrap();
raw::RedisModule_ReplyWithSimpleString.unwrap()(self.ctx, msg.as_ptr()).into()
},
Ok(RedisValue::SimpleString(s)) => unsafe {
let msg = CString::new(s).unwrap();
raw::RedisModule_ReplyWithSimpleString.unwrap()(self.ctx, msg.as_ptr()).into()
},
Ok(RedisValue::BulkString(s)) => unsafe {
raw::RedisModule_ReplyWithString.unwrap()(
self.ctx,
RedisString::create(self.ctx, s.as_ref()).inner,
)
.into()
},
Ok(RedisValue::Array(array)) => {
unsafe {
raw::RedisModule_ReplyWithArray.unwrap()(self.ctx, array.len() as c_long);
}
for elem in array {
self.reply(Ok(elem));
}
raw::Status::Ok
}
Ok(RedisValue::Null) => unsafe {
raw::RedisModule_ReplyWithNull.unwrap()(self.ctx).into()
},
Ok(RedisValue::NoReply) => raw::Status::Ok,
Err(RedisError::WrongArity) => unsafe {
if self.is_keys_position_request() {
raw::Status::Err
} else {
raw::RedisModule_WrongArity.unwrap()(self.ctx).into()
}
},
Err(RedisError::WrongType) => unsafe {
let msg = CString::new(RedisError::WrongType.to_string()).unwrap();
raw::RedisModule_ReplyWithError.unwrap()(self.ctx, msg.as_ptr()).into()
},
Err(RedisError::String(s)) => unsafe {
let msg = CString::new(s).unwrap();
raw::RedisModule_ReplyWithError.unwrap()(self.ctx, msg.as_ptr()).into()
},
Err(RedisError::Str(s)) => unsafe {
let msg = CString::new(s).unwrap();
raw::RedisModule_ReplyWithError.unwrap()(self.ctx, msg.as_ptr()).into()
},
}
}
pub fn open_key(&self, key: &str) -> RedisKey {
RedisKey::open(self.ctx, key)
}
pub fn open_with_redis_string(&self, key: *mut raw::RedisModuleString) -> RedisKeyWritable {
RedisKeyWritable::open_with_redis_string(self.ctx, key)
}
pub fn open_key_writable(&self, key: &str) -> RedisKeyWritable {
RedisKeyWritable::open(self.ctx, key)
}
pub fn replicate_verbatim(&self) {
raw::replicate_verbatim(self.ctx);
}
pub fn create_string(&self, s: &str) -> RedisString {
RedisString::create(self.ctx, s)
}
pub fn get_raw(&self) -> *mut raw::RedisModuleCtx {
self.ctx
}
#[cfg(feature = "experimental-api")]
pub fn export_shared_api(
&self,
func: *const ::std::os::raw::c_void,
name: *const ::std::os::raw::c_char,
) {
raw::export_shared_api(self.ctx, func, name);
}
#[cfg(feature = "experimental-api")]
pub fn notify_keyspace_event(
&self,
event_type: raw::NotifyEvent,
event: &str,
keyname: &str,
) -> raw::Status {
raw::notify_keyspace_event(self.ctx, event_type, event, keyname)
}
}