redis_module_ext/data_type/
mod.rs1use std::collections::HashMap;
2use std::ffi::CString;
3use std::sync::{LazyLock, RwLock};
4
5use redis_module::native_types::RedisType;
6use redis_module::raw;
7
8mod io;
9pub use io::*;
10mod methods;
11pub use methods::*;
12
13struct RedisModuleType {
14 ptr: *mut raw::RedisModuleType,
15}
16
17unsafe impl Send for RedisModuleType {}
18unsafe impl Sync for RedisModuleType {}
19
20pub trait RedisDataType: Sized {
21 #[doc(hidden)]
22 const NAME: &'static str;
23 #[doc(hidden)]
24 const VERSION: i32;
25
26 #[doc(hidden)]
27 fn module_methods(ctx: &redis_module::Context) -> raw::RedisModuleTypeMethods;
28
29 #[doc(hidden)]
30 fn register(ctx: &redis_module::Context) -> Result<(), &'static str> {
31 if Self::NAME.len() != 9 {
32 let msg = "Redis requires the length of native type names to be exactly 9 characters";
33 ctx.log_warning(&format!("{msg}, name is: '{}'", Self::NAME));
34 return Err(msg);
35 }
36
37 let name = CString::new(Self::NAME).unwrap();
38
39 let redis_type = unsafe {
40 raw::RedisModule_CreateDataType.unwrap()(
41 ctx.ctx,
42 name.as_ptr(),
43 Self::VERSION, &mut Self::module_methods(ctx),
45 )
46 };
47
48 if Self::redis_ty(redis_type).is_none() {
49 ctx.log_warning("Error: created data type is null");
50 return Err("Error: created data type is null");
51 }
52
53 ctx.log_debug(format!("Created new data type '{}'", Self::NAME).as_str());
54
55 Ok(())
56 }
57
58 #[doc(hidden)]
59 fn redis_ty(ptr: *mut raw::RedisModuleType) -> Option<redis_module::native_types::RedisType> {
60 static TYPE_REGISTRY: LazyLock<RwLock<HashMap<&'static str, RedisModuleType>>> = LazyLock::new(Default::default);
61
62 if !ptr.is_null() {
63 TYPE_REGISTRY.write().unwrap().insert(Self::NAME, RedisModuleType { ptr });
64 }
65
66 let ptr = TYPE_REGISTRY.read().unwrap().get(Self::NAME)?.ptr;
67
68 let ty = RedisType::new(Self::NAME, Self::VERSION, unsafe { std::mem::zeroed() });
69 ty.raw_type.replace(ptr);
70 Some(ty)
71 }
72}