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 unsafe { raw::Export_RedisModule_Init(
240 ctx,
241 name_buffer.as_ptr().cast::<c_char>(),
242 module_version,
243 raw::REDISMODULE_APIVER_1 as c_int,
244 ) } == raw::Status::Err as c_int { return raw::Status::Err as c_int; }
245
246 let context = $crate::Context::new(ctx);
247 unsafe {
248 let _ = $crate::MODULE_CONTEXT.set_context(&context);
249 }
250 let args = $crate::decode_args(ctx, argv, argc);
251
252 $(
253 if (&$data_type).create_data_type(ctx).is_err() {
254 return raw::Status::Err as c_int;
255 }
256 )*
257
258 $(
259 $(
260 #[cfg(feature = "min-valkey-compatibility-version-8-0")]
261 context.add_acl_category($acl_category);
262 )*
263 )?
264
265 $(
266 $crate::redis_command!(ctx, $name, $command, $flags, $firstkey, $lastkey, $keystep $(, $command_acl_categories)?);
267 )*
268
269 if $crate::commands::register_commands(&context) == raw::Status::Err {
270 return raw::Status::Err as c_int;
271 }
272
273 $(
274 $(
275 $crate::redis_event_handler!(ctx, $(raw::NotifyEvent::$event_type |)+ raw::NotifyEvent::empty(), $event_handler);
276 )*
277 )?
278
279 $(
280 $(
281 $(
282 let default = if $use_module_args {
283 match get_i64_default_config_value(&args, $i64_configuration_name, $i64_default) {
284 Ok(v) => v,
285 Err(e) => {
286 context.log_warning(&format!("{e}"));
287 return raw::Status::Err as c_int;
288 }
289 }
290 } else {
291 $i64_default
292 };
293 let mut use_fallback = true;
294 $(
295 use_fallback = false;
296 register_i64_configuration(&context, $i64_configuration_name, $i64_configuration_val, default, $i64_min, $i64_max, $i64_flags_options, $i64_on_changed, $i64_on_set);
297 )?
298 if (use_fallback) {
299 register_i64_configuration(&context, $i64_configuration_name, $i64_configuration_val, default, $i64_min, $i64_max, $i64_flags_options, $i64_on_changed, None);
300 }
301 )*
302 )?
303 $(
304 $(
305 let default = if $use_module_args {
306 match get_string_default_config_value(&args, $string_configuration_name, $string_default) {
307 Ok(v) => v,
308 Err(e) => {
309 context.log_warning(&format!("{e}"));
310 return raw::Status::Err as c_int;
311 }
312 }
313 } else {
314 $string_default
315 };
316 let mut use_fallback = true;
317 $(
318 use_fallback = false;
319 register_string_configuration(&context, $string_configuration_name, $string_configuration_val, default, $string_flags_options, $string_on_changed, $string_on_set);
320 )?
321 if (use_fallback) {
322 register_string_configuration(&context, $string_configuration_name, $string_configuration_val, default, $string_flags_options, $string_on_changed, None);
323 }
324 )*
325 )?
326 $(
327 $(
328 let default = if $use_module_args {
329 match get_bool_default_config_value(&args, $bool_configuration_name, $bool_default) {
330 Ok(v) => v,
331 Err(e) => {
332 context.log_warning(&format!("{e}"));
333 return raw::Status::Err as c_int;
334 }
335 }
336 } else {
337 $bool_default
338 };
339 let mut use_fallback = true;
340 $(
341 use_fallback = false;
342 register_bool_configuration(&context, $bool_configuration_name, $bool_configuration_val, default, $bool_flags_options, $bool_on_changed, $bool_on_set);
343 )?
344 if (use_fallback) {
345 register_bool_configuration(&context, $bool_configuration_name, $bool_configuration_val, default, $bool_flags_options, $bool_on_changed, None);
346 }
347 )*
348 )?
349 $(
350 $(
351 let default = if $use_module_args {
352 match get_enum_default_config_value(&args, $enum_configuration_name, $enum_default) {
353 Ok(v) => v,
354 Err(e) => {
355 context.log_warning(&format!("{e}"));
356 return raw::Status::Err as c_int;
357 }
358 }
359 } else {
360 $enum_default
361 };
362 let mut use_fallback = true;
363 $(
364 use_fallback = false;
365 register_enum_configuration(&context, $enum_configuration_name, $enum_configuration_val, default.clone(), $enum_flags_options, $enum_on_changed, $enum_on_set);
366 )?
367 if (use_fallback) {
368 register_enum_configuration(&context, $enum_configuration_name, $enum_configuration_val, default.clone(), $enum_flags_options, $enum_on_changed, None);
369 }
370 )*
371 )?
372 raw::RedisModule_LoadConfigs.unwrap()(ctx);
373
374 $(
375 $crate::redis_command!(ctx, $module_config_get_command, |ctx, args: Vec<RedisString>| {
376 module_config_get(ctx, args, $module_name)
377 }, "", 0, 0, 0);
378 )?
379
380 $(
381 $crate::redis_command!(ctx, $module_config_set_command, |ctx, args: Vec<RedisString>| {
382 module_config_set(ctx, args, $module_name)
383 }, "", 0, 0, 0);
384 )?
385 )?
386
387 raw::register_info_function(ctx, Some(__info_func));
388
389 if let Err(e) = register_server_events(&context) {
390 context.log_warning(&format!("{e}"));
391 return raw::Status::Err as c_int;
392 }
393
394 $(
395 if $init_func(&context, &args) == $crate::Status::Err {
396 return $crate::Status::Err as c_int;
397 }
398 )*
399
400 raw::Status::Ok as c_int
401 }
402
403 #[no_mangle]
404 #[allow(non_snake_case)]
405 pub extern "C" fn RedisModule_OnUnload(
406 ctx: *mut $crate::raw::RedisModuleCtx
407 ) -> std::os::raw::c_int {
408 use std::os::raw::c_int;
409
410 let context = $crate::Context::new(ctx);
411 $(
412 if $deinit_func(&context) == $crate::Status::Err {
413 return $crate::Status::Err as c_int;
414 }
415 )*
416
417 $crate::raw::Status::Ok as c_int
418 }
419 }
420}