1#[macro_export]
2macro_rules! redis_command {
3 ($ctx:expr,
4 $command_name:expr,
5 $command_handler:expr,
6 $command_flags:expr,
7 $firstkey:expr,
8 $lastkey:expr,
9 $keystep:expr) => {{
10 let name = CString::new($command_name).unwrap();
11 let flags = CString::new($command_flags).unwrap();
12
13 extern "C" fn __do_command(
15 ctx: *mut $crate::raw::RedisModuleCtx,
16 argv: *mut *mut $crate::raw::RedisModuleString,
17 argc: c_int,
18 ) -> c_int {
19 let context = $crate::Context::new(ctx);
20
21 let args = $crate::decode_args(ctx, argv, argc);
22 let response = $command_handler(&context, args);
23 context.reply(response.map(|v| v.into())) as c_int
24 }
25 if unsafe {
28 $crate::raw::RedisModule_CreateCommand.unwrap()(
29 $ctx,
30 name.as_ptr(),
31 Some(__do_command),
32 flags.as_ptr(),
33 $firstkey,
34 $lastkey,
35 $keystep,
36 )
37 } == $crate::raw::Status::Err as c_int
38 {
39 return $crate::raw::Status::Err as c_int;
40 }
41 }};
42}
43
44#[macro_export]
45macro_rules! redis_event_handler {
46 (
47 $ctx: expr,
48 $event_type: expr,
49 $event_handler: expr
50 ) => {{
51 extern "C" fn __handle_event(
52 ctx: *mut $crate::raw::RedisModuleCtx,
53 event_type: c_int,
54 event: *const c_char,
55 key: *mut $crate::raw::RedisModuleString,
56 ) -> c_int {
57 let context = $crate::Context::new(ctx);
58
59 let redis_key = $crate::RedisString::string_as_slice(key);
60 let event_str = unsafe { CStr::from_ptr(event) };
61 $event_handler(
62 &context,
63 $crate::NotifyEvent::from_bits_truncate(event_type),
64 event_str.to_str().unwrap(),
65 redis_key,
66 );
67
68 $crate::raw::Status::Ok as c_int
69 }
70
71 if unsafe {
72 $crate::raw::RedisModule_SubscribeToKeyspaceEvents.unwrap()(
73 $ctx,
74 $event_type.bits(),
75 Some(__handle_event),
76 )
77 } == $crate::raw::Status::Err as c_int
78 {
79 return $crate::raw::Status::Err as c_int;
80 }
81 }};
82}
83
84#[macro_export]
89macro_rules! redis_module {
90 (
91 name: $module_name:expr,
92 version: $module_version:expr,
93 allocator: ($allocator_type:ty, $allocator_init:expr),
97 data_types: [
98 $($data_type:ident),* $(,)*
99 ],
100 $(init: $init_func:ident,)* $(,)*
101 $(deinit: $deinit_func:ident,)* $(,)*
102 $(info: $info_func:ident,)?
103 commands: [
104 $([
105 $name:expr,
106 $command:expr,
107 $flags:expr,
108 $firstkey:expr,
109 $lastkey:expr,
110 $keystep:expr
111 ]),* $(,)*
112 ] $(,)*
113 $(event_handlers: [
114 $([
115 $(@$event_type:ident) +:
116 $event_handler:expr
117 ]),* $(,)*
118 ] $(,)* )?
119 $(configurations: [
120 $(i64:[$([
121 $i64_configuration_name:expr,
122 $i64_configuration_val:expr,
123 $i64_default:expr,
124 $i64_min:expr,
125 $i64_max:expr,
126 $i64_flags_options:expr,
127 $i64_on_changed:expr
128 ]),* $(,)*],)?
129 $(string:[$([
130 $string_configuration_name:expr,
131 $string_configuration_val:expr,
132 $string_default:expr,
133 $string_flags_options:expr,
134 $string_on_changed:expr
135 ]),* $(,)*],)?
136 $(bool:[$([
137 $bool_configuration_name:expr,
138 $bool_configuration_val:expr,
139 $bool_default:expr,
140 $bool_flags_options:expr,
141 $bool_on_changed:expr
142 ]),* $(,)*],)?
143 $(enum:[$([
144 $enum_configuration_name:expr,
145 $enum_configuration_val:expr,
146 $enum_default:expr,
147 $enum_flags_options:expr,
148 $enum_on_changed:expr
149 ]),* $(,)*],)?
150 $(module_args_as_configuration:$use_module_args:expr,)?
151 $(module_config_get:$module_config_get_command:expr,)?
152 $(module_config_set:$module_config_set_command:expr,)?
153 ])?
154 ) => {
155 #[global_allocator]
157 static REDIS_MODULE_ALLOCATOR: $allocator_type = $allocator_init;
158
159 $(
161 #[redis_module_macros::info_command_handler]
162 #[inline]
163 fn module_info(ctx: &InfoContext, for_crash_report: bool) -> RedisResult<()> {
164 $info_func(ctx, for_crash_report);
165
166 Ok(())
167 }
168 )?
169
170 extern "C" fn __info_func(
171 ctx: *mut $crate::raw::RedisModuleInfoCtx,
172 for_crash_report: i32,
173 ) {
174 $crate::basic_info_command_handler(&$crate::InfoContext::new(ctx), for_crash_report == 1);
175 }
176
177 #[no_mangle]
178 #[allow(non_snake_case)]
179 pub unsafe extern "C" fn RedisModule_OnLoad(
180 ctx: *mut $crate::raw::RedisModuleCtx,
181 argv: *mut *mut $crate::raw::RedisModuleString,
182 argc: std::os::raw::c_int,
183 ) -> std::os::raw::c_int {
184 use std::os::raw::{c_int, c_char};
185 use std::ffi::{CString, CStr};
186
187 use $crate::raw;
188 use $crate::RedisString;
189 use $crate::server_events::register_server_events;
190 use $crate::configuration::register_i64_configuration;
191 use $crate::configuration::register_string_configuration;
192 use $crate::configuration::register_bool_configuration;
193 use $crate::configuration::register_enum_configuration;
194 use $crate::configuration::module_config_get;
195 use $crate::configuration::module_config_set;
196 use $crate::configuration::get_i64_default_config_value;
197 use $crate::configuration::get_string_default_config_value;
198 use $crate::configuration::get_bool_default_config_value;
199 use $crate::configuration::get_enum_default_config_value;
200
201 let mut name_buffer = [0; 64];
205 unsafe {
206 std::ptr::copy(
207 $module_name.as_ptr(),
208 name_buffer.as_mut_ptr(),
209 $module_name.len(),
210 );
211 }
212
213 let module_version = $module_version as c_int;
214
215 if unsafe { raw::Export_RedisModule_Init(
216 ctx,
217 name_buffer.as_ptr().cast::<c_char>(),
218 module_version,
219 raw::REDISMODULE_APIVER_1 as c_int,
220 ) } == raw::Status::Err as c_int { return raw::Status::Err as c_int; }
221
222 let context = $crate::Context::new(ctx);
223 unsafe {
224 let _ = $crate::MODULE_CONTEXT.set_context(&context);
225 }
226 let args = $crate::decode_args(ctx, argv, argc);
227
228 $(
229 if (&$data_type).create_data_type(ctx).is_err() {
230 return raw::Status::Err as c_int;
231 }
232 )*
233
234 $(
235 $crate::redis_command!(ctx, $name, $command, $flags, $firstkey, $lastkey, $keystep);
236 )*
237
238 if $crate::commands::register_commands(&context) == raw::Status::Err {
239 return raw::Status::Err as c_int;
240 }
241
242 $(
243 $(
244 $crate::redis_event_handler!(ctx, $(raw::NotifyEvent::$event_type |)+ raw::NotifyEvent::empty(), $event_handler);
245 )*
246 )?
247
248 $(
249 $(
250 $(
251 let default = if $use_module_args {
252 match get_i64_default_config_value(&args, $i64_configuration_name, $i64_default) {
253 Ok(v) => v,
254 Err(e) => {
255 context.log_warning(&format!("{e}"));
256 return raw::Status::Err as c_int;
257 }
258 }
259 } else {
260 $i64_default
261 };
262 register_i64_configuration(&context, $i64_configuration_name, $i64_configuration_val, default, $i64_min, $i64_max, $i64_flags_options, $i64_on_changed);
263 )*
264 )?
265 $(
266 $(
267 let default = if $use_module_args {
268 match get_string_default_config_value(&args, $string_configuration_name, $string_default) {
269 Ok(v) => v,
270 Err(e) => {
271 context.log_warning(&format!("{e}"));
272 return raw::Status::Err as c_int;
273 }
274 }
275 } else {
276 $string_default
277 };
278 register_string_configuration(&context, $string_configuration_name, $string_configuration_val, default, $string_flags_options, $string_on_changed);
279 )*
280 )?
281 $(
282 $(
283 let default = if $use_module_args {
284 match get_bool_default_config_value(&args, $bool_configuration_name, $bool_default) {
285 Ok(v) => v,
286 Err(e) => {
287 context.log_warning(&format!("{e}"));
288 return raw::Status::Err as c_int;
289 }
290 }
291 } else {
292 $bool_default
293 };
294 register_bool_configuration(&context, $bool_configuration_name, $bool_configuration_val, default, $bool_flags_options, $bool_on_changed);
295 )*
296 )?
297 $(
298 $(
299 let default = if $use_module_args {
300 match get_enum_default_config_value(&args, $enum_configuration_name, $enum_default) {
301 Ok(v) => v,
302 Err(e) => {
303 context.log_warning(&format!("{e}"));
304 return raw::Status::Err as c_int;
305 }
306 }
307 } else {
308 $enum_default
309 };
310 register_enum_configuration(&context, $enum_configuration_name, $enum_configuration_val, default, $enum_flags_options, $enum_on_changed);
311 )*
312 )?
313 raw::RedisModule_LoadConfigs.unwrap()(ctx);
314
315 $(
316 $crate::redis_command!(ctx, $module_config_get_command, |ctx, args: Vec<RedisString>| {
317 module_config_get(ctx, args, $module_name)
318 }, "", 0, 0, 0);
319 )?
320
321 $(
322 $crate::redis_command!(ctx, $module_config_set_command, |ctx, args: Vec<RedisString>| {
323 module_config_set(ctx, args, $module_name)
324 }, "", 0, 0, 0);
325 )?
326 )?
327
328 raw::register_info_function(ctx, Some(__info_func));
329
330 if let Err(e) = register_server_events(&context) {
331 context.log_warning(&format!("{e}"));
332 return raw::Status::Err as c_int;
333 }
334
335 $(
336 if $init_func(&context, &args) == $crate::Status::Err {
337 return $crate::Status::Err as c_int;
338 }
339 )*
340
341 raw::Status::Ok as c_int
342 }
343
344 #[no_mangle]
345 #[allow(non_snake_case)]
346 pub extern "C" fn RedisModule_OnUnload(
347 ctx: *mut $crate::raw::RedisModuleCtx
348 ) -> std::os::raw::c_int {
349 use std::os::raw::c_int;
350
351 let context = $crate::Context::new(ctx);
352 $(
353 if $deinit_func(&context) == $crate::Status::Err {
354 return $crate::Status::Err as c_int;
355 }
356 )*
357
358 $crate::raw::Status::Ok as c_int
359 }
360 }
361}