use std::convert::TryInto;
use std::ffi::c_void;
use std::time::Duration;
use crate::raw;
use crate::raw::RedisModuleTimerID;
use crate::{Context, RedisError};
#[repr(C)]
struct CallbackData<F: FnOnce(&Context, T), T> {
data: T,
callback: F,
}
impl Context {
pub fn create_timer<F, T>(&self, period: Duration, callback: F, data: T) -> RedisModuleTimerID
where
F: FnOnce(&Context, T),
{
let cb_data = CallbackData { data, callback };
let data = Box::from(cb_data);
let data = Box::into_raw(data);
let timer_id = unsafe {
raw::RedisModule_CreateTimer.unwrap()(
self.ctx,
period
.as_millis()
.try_into()
.expect("Value must fit in 64 bits"),
Some(raw_callback::<F, T>),
data as *mut c_void,
)
};
timer_id
}
pub fn stop_timer<T>(&self, timer_id: RedisModuleTimerID) -> Result<T, RedisError> {
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();
if status != raw::Status::Ok {
return Err(RedisError::Str(
"RedisModule_StopTimer failed, timer may not exist",
));
}
let data: T = take_data(data);
return Ok(data);
}
pub fn get_timer_info<T>(
&self,
timer_id: RedisModuleTimerID,
) -> Result<(Duration, &T), RedisError> {
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();
if status != raw::Status::Ok {
return Err(RedisError::Str(
"RedisModule_GetTimerInfo failed, timer may not exist",
));
}
let data = data as *mut T;
let data = unsafe { &*data };
Ok((Duration::from_millis(remaining), data))
}
}
fn take_data<T>(data: *mut c_void) -> T {
let data = data as *mut T;
let data = unsafe { Box::from_raw(data) };
*data
}
extern "C" fn raw_callback<F, T>(ctx: *mut raw::RedisModuleCtx, data: *mut c_void)
where
F: FnOnce(&Context, T),
{
let ctx = &Context::new(ctx);
if data.is_null() {
ctx.log_debug("[callback] Data is null; this should not happen!");
return;
}
let cb_data: CallbackData<F, T> = take_data(data);
(cb_data.callback)(ctx, cb_data.data);
}