1#[macro_export]
2macro_rules! redis_command {
3 (
4 $ctx:expr,
5 $command_name:expr,
6 $command_handler:expr,
7 $command_flags:expr,
8 $firstkey:expr,
9 $lastkey:expr,
10 $keystep:expr
11 $(,
12 $command_acl_categories:expr
13 )?
14 ) => {{
15 let name = CString::new($command_name).unwrap();
16 let flags = CString::new($command_flags).unwrap();
17 extern "C" fn __do_command(
19 ctx: *mut $crate::raw::RedisModuleCtx,
20 argv: *mut *mut $crate::raw::RedisModuleString,
21 argc: c_int,
22 ) -> c_int {
23 let context = $crate::Context::new(ctx);
24 let args = $crate::decode_args(ctx, argv, argc);
25 let response = $command_handler(&context, args);
26 context.reply(response.map(|v| v.into())) as c_int
27 }
28
29 if unsafe {
30 $crate::raw::RedisModule_CreateCommand.unwrap()(
31 $ctx,
32 name.as_ptr(),
33 Some(__do_command),
34 flags.as_ptr(),
35 $firstkey,
36 $lastkey,
37 $keystep,
38 )
39 } == $crate::raw::Status::Err as c_int
40 {
41 return $crate::raw::Status::Err as c_int;
42 }
43
44 $(
45 let context = $crate::Context::new($ctx);
46 let acl_categories_to_add = CString::new($command_acl_categories).unwrap();
47 #[cfg(feature = "min-valkey-compatibility-version-8-0")]
48 context.set_acl_category(name.as_ptr(), acl_categories_to_add.as_ptr());
49 )?
50 }};
51}
52
53#[macro_export]
54macro_rules! redis_event_handler {
55 (
56 $ctx: expr,
57 $event_type: expr,
58 $event_handler: expr
59 ) => {{
60 extern "C" fn __handle_event(
61 ctx: *mut $crate::raw::RedisModuleCtx,
62 event_type: c_int,
63 event: *const c_char,
64 key: *mut $crate::raw::RedisModuleString,
65 ) -> c_int {
66 let context = $crate::Context::new(ctx);
67
68 let redis_key = $crate::ValkeyString::string_as_slice(key);
69 let event_str = unsafe { CStr::from_ptr(event) };
70 $event_handler(
71 &context,
72 $crate::NotifyEvent::from_bits_truncate(event_type),
73 event_str.to_str().unwrap(),
74 redis_key,
75 );
76
77 $crate::raw::Status::Ok as c_int
78 }
79
80 let all_available_notification_flags = $crate::raw::get_keyspace_notification_flags_all();
81 let available_wanted_notification_flags = $event_type.intersection(all_available_notification_flags);
82 if !all_available_notification_flags.contains($event_type) {
83 let not_supported = $event_type.difference(all_available_notification_flags);
84 $crate::Context::new($ctx).log_notice(&format!(
85 "These event notification flags set aren't supported: {not_supported:?}. These flags will be used: {available_wanted_notification_flags:?}"
86 ));
87 }
88
89 if !available_wanted_notification_flags.is_empty() && unsafe {
90 $crate::raw::RedisModule_SubscribeToKeyspaceEvents.unwrap()(
91 $ctx,
92 available_wanted_notification_flags.bits(),
93 Some(__handle_event),
94 )
95 } == $crate::raw::Status::Err as c_int
96 {
97 return $crate::raw::Status::Err as c_int;
98 }
99 }};
100}
101
102#[macro_export]
107macro_rules! valkey_module {
108 (
109 name: $module_name:expr,
110 version: $module_version:expr,
111 allocator: ($allocator_type:ty, $allocator_init:expr),
115 data_types: [
116 $($data_type:ident),* $(,)*
117 ],
118 $(init: $init_func:ident,)* $(,)*
119 $(deinit: $deinit_func:ident,)* $(,)*
120 $(info: $info_func:ident,)?
121 $(acl_categories: [
122 $($acl_category:expr),* $(,)*
123 ])?
124 commands: [
125 $([
126 $name:expr,
127 $command:expr,
128 $flags:expr,
129 $firstkey:expr,
130 $lastkey:expr,
131 $keystep:expr
132 $(,
133 $command_acl_categories:expr
134 )?
135 ]),* $(,)?
136 ] $(,)*
137 $(event_handlers: [
138 $([
139 $(@$event_type:ident) +:
140 $event_handler:expr
141 ]),* $(,)*
142 ] $(,)* )?
143 $(configurations: [
144 $(i64:[$([
145 $i64_configuration_name:expr,
146 $i64_configuration_val:expr,
147 $i64_default:expr,
148 $i64_min:expr,
149 $i64_max:expr,
150 $i64_flags_options:expr,
151 $i64_on_changed:expr $(, $i64_on_set:expr)?
152 ]),* $(,)*],)?
153 $(string:[$([
154 $string_configuration_name:expr,
155 $string_configuration_val:expr,
156 $string_default:expr,
157 $string_flags_options:expr,
158 $string_on_changed:expr $(, $string_on_set:expr)?
159 ]),* $(,)*],)?
160 $(bool:[$([
161 $bool_configuration_name:expr,
162 $bool_configuration_val:expr,
163 $bool_default:expr,
164 $bool_flags_options:expr,
165 $bool_on_changed:expr $(, $bool_on_set:expr)?
166 ]),* $(,)*],)?
167 $(enum:[$([
168 $enum_configuration_name:expr,
169 $enum_configuration_val:expr,
170 $enum_default:expr,
171 $enum_flags_options:expr,
172 $enum_on_changed:expr $(, $enum_on_set:expr)?
173 ]),* $(,)*],)?
174 $(module_args_as_configuration:$use_module_args:expr,)?
175 $(module_config_get:$module_config_get_command:expr,)?
176 $(module_config_set:$module_config_set_command:expr,)?
177 ])?
178 ) => {
179 #[global_allocator]
181 static REDIS_MODULE_ALLOCATOR: $allocator_type = $allocator_init;
182
183 $(
185 #[valkey_module_macros::info_command_handler]
186 #[inline]
187 fn module_info(ctx: &InfoContext, for_crash_report: bool) -> ValkeyResult<()> {
188 $info_func(ctx, for_crash_report);
189
190 Ok(())
191 }
192 )?
193
194 extern "C" fn __info_func(
195 ctx: *mut $crate::raw::RedisModuleInfoCtx,
196 for_crash_report: i32,
197 ) {
198 $crate::basic_info_command_handler(&$crate::InfoContext::new(ctx), for_crash_report == 1);
199 }
200
201 #[no_mangle]
202 #[allow(non_snake_case)]
203 pub unsafe extern "C" fn RedisModule_OnLoad(
204 ctx: *mut $crate::raw::RedisModuleCtx,
205 argv: *mut *mut $crate::raw::RedisModuleString,
206 argc: std::os::raw::c_int,
207 ) -> std::os::raw::c_int {
208 use std::os::raw::{c_int, c_char};
209 use std::ffi::{CString, CStr};
210
211 use $crate::raw;
212 use $crate::ValkeyString;
213 use $crate::server_events::register_server_events;
214 use $crate::configuration::register_i64_configuration;
215 use $crate::configuration::register_string_configuration;
216 use $crate::configuration::register_bool_configuration;
217 use $crate::configuration::register_enum_configuration;
218 use $crate::configuration::module_config_get;
219 use $crate::configuration::module_config_set;
220 use $crate::configuration::get_i64_default_config_value;
221 use $crate::configuration::get_string_default_config_value;
222 use $crate::configuration::get_bool_default_config_value;
223 use $crate::configuration::get_enum_default_config_value;
224
225 let mut name_buffer = [0; 64];
229 unsafe {
230 std::ptr::copy(
231 $module_name.as_ptr(),
232 name_buffer.as_mut_ptr(),
233 $module_name.len(),
234 );
235 }
236
237 let module_version = $module_version as c_int;
238
239 if !raw::use_redis_module_api() {
243 let status = unsafe {
244 raw::Export_ValkeyModule_Init(
245 ctx as *mut raw::ValkeyModuleCtx,
246 name_buffer.as_ptr().cast::<c_char>(),
247 module_version,
248 raw::VALKEYMODULE_APIVER_1 as c_int,
249 )
250 };
251 if status == raw::Status::Err as c_int {
252 return raw::Status::Err as c_int;
253 }
254 }
255
256 let status = unsafe {
260 raw::Export_RedisModule_Init(
261 ctx,
262 name_buffer.as_ptr().cast::<c_char>(),
263 module_version,
264 raw::REDISMODULE_APIVER_1 as c_int,
265 )
266 };
267 if status == raw::Status::Err as c_int {
268 return raw::Status::Err as c_int;
269 }
270
271 let context = $crate::Context::new(ctx);
272 unsafe {
273 let _ = $crate::MODULE_CONTEXT.set_context(&context);
274 }
275 let args = $crate::decode_args(ctx, argv, argc);
276
277 $(
278 if (&$data_type).create_data_type(ctx).is_err() {
279 return raw::Status::Err as c_int;
280 }
281 )*
282
283 $(
284 $(
285 #[cfg(feature = "min-valkey-compatibility-version-8-0")]
286 context.add_acl_category($acl_category);
287 )*
288 )?
289
290 $(
291 $crate::redis_command!(ctx, $name, $command, $flags, $firstkey, $lastkey, $keystep $(, $command_acl_categories)?);
292 )*
293
294 if $crate::commands::register_commands(&context) == raw::Status::Err {
295 return raw::Status::Err as c_int;
296 }
297
298 $(
299 $(
300 $crate::redis_event_handler!(ctx, $(raw::NotifyEvent::$event_type |)+ raw::NotifyEvent::empty(), $event_handler);
301 )*
302 )?
303
304 $(
305 $(
306 $(
307 let default = if $use_module_args {
308 match get_i64_default_config_value(&args, $i64_configuration_name, $i64_default) {
309 Ok(v) => v,
310 Err(e) => {
311 context.log_warning(&format!("{e}"));
312 return raw::Status::Err as c_int;
313 }
314 }
315 } else {
316 $i64_default
317 };
318 let mut use_fallback = true;
319 $(
320 use_fallback = false;
321 register_i64_configuration(&context, $i64_configuration_name, $i64_configuration_val, default, $i64_min, $i64_max, $i64_flags_options, $i64_on_changed, $i64_on_set);
322 )?
323 if (use_fallback) {
324 register_i64_configuration(&context, $i64_configuration_name, $i64_configuration_val, default, $i64_min, $i64_max, $i64_flags_options, $i64_on_changed, None);
325 }
326 )*
327 )?
328 $(
329 $(
330 let default = if $use_module_args {
331 match get_string_default_config_value(&args, $string_configuration_name, $string_default) {
332 Ok(v) => v,
333 Err(e) => {
334 context.log_warning(&format!("{e}"));
335 return raw::Status::Err as c_int;
336 }
337 }
338 } else {
339 $string_default
340 };
341 let mut use_fallback = true;
342 $(
343 use_fallback = false;
344 register_string_configuration(&context, $string_configuration_name, $string_configuration_val, default, $string_flags_options, $string_on_changed, $string_on_set);
345 )?
346 if (use_fallback) {
347 register_string_configuration(&context, $string_configuration_name, $string_configuration_val, default, $string_flags_options, $string_on_changed, None);
348 }
349 )*
350 )?
351 $(
352 $(
353 let default = if $use_module_args {
354 match get_bool_default_config_value(&args, $bool_configuration_name, $bool_default) {
355 Ok(v) => v,
356 Err(e) => {
357 context.log_warning(&format!("{e}"));
358 return raw::Status::Err as c_int;
359 }
360 }
361 } else {
362 $bool_default
363 };
364 let mut use_fallback = true;
365 $(
366 use_fallback = false;
367 register_bool_configuration(&context, $bool_configuration_name, $bool_configuration_val, default, $bool_flags_options, $bool_on_changed, $bool_on_set);
368 )?
369 if (use_fallback) {
370 register_bool_configuration(&context, $bool_configuration_name, $bool_configuration_val, default, $bool_flags_options, $bool_on_changed, None);
371 }
372 )*
373 )?
374 $(
375 $(
376 let default = if $use_module_args {
377 match get_enum_default_config_value(&args, $enum_configuration_name, $enum_default) {
378 Ok(v) => v,
379 Err(e) => {
380 context.log_warning(&format!("{e}"));
381 return raw::Status::Err as c_int;
382 }
383 }
384 } else {
385 $enum_default
386 };
387 let mut use_fallback = true;
388 $(
389 use_fallback = false;
390 register_enum_configuration(&context, $enum_configuration_name, $enum_configuration_val, default.clone(), $enum_flags_options, $enum_on_changed, $enum_on_set);
391 )?
392 if (use_fallback) {
393 register_enum_configuration(&context, $enum_configuration_name, $enum_configuration_val, default.clone(), $enum_flags_options, $enum_on_changed, None);
394 }
395 )*
396 )?
397 raw::RedisModule_LoadConfigs.unwrap()(ctx);
398
399 $(
400 $crate::redis_command!(ctx, $module_config_get_command, |ctx, args: Vec<RedisString>| {
401 module_config_get(ctx, args, $module_name)
402 }, "", 0, 0, 0);
403 )?
404
405 $(
406 $crate::redis_command!(ctx, $module_config_set_command, |ctx, args: Vec<RedisString>| {
407 module_config_set(ctx, args, $module_name)
408 }, "", 0, 0, 0);
409 )?
410 )?
411
412 raw::register_info_function(ctx, Some(__info_func));
413
414 if let Err(e) = register_server_events(&context) {
415 context.log_warning(&format!("{e}"));
416 return raw::Status::Err as c_int;
417 }
418
419 $(
420 if $init_func(&context, &args) == $crate::Status::Err {
421 return $crate::Status::Err as c_int;
422 }
423 )*
424
425 raw::Status::Ok as c_int
426 }
427
428 #[no_mangle]
429 #[allow(non_snake_case)]
430 pub extern "C" fn RedisModule_OnUnload(
431 ctx: *mut $crate::raw::RedisModuleCtx
432 ) -> std::os::raw::c_int {
433 use std::os::raw::c_int;
434
435 let context = $crate::Context::new(ctx);
436 $(
437 if $deinit_func(&context) == $crate::Status::Err {
438 return $crate::Status::Err as c_int;
439 }
440 )*
441
442 $crate::raw::Status::Ok as c_int
443 }
444 }
445}