redis_module/
macros.rs

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        /////////////////////
14        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        /////////////////////
26
27        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/// Defines a Redis module.
85///
86/// It registers the defined module, sets it up and initialises properly,
87/// registers all the commands and types.
88#[macro_export]
89macro_rules! redis_module {
90    (
91        name: $module_name:expr,
92        version: $module_version:expr,
93        /// Global allocator for the redis module defined.
94        /// In most of the cases, the Redis allocator ([crate::alloc::RedisAlloc])
95        /// should be used.
96        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        /// Redis module allocator.
156        #[global_allocator]
157        static REDIS_MODULE_ALLOCATOR: $allocator_type = $allocator_init;
158
159        // The old-style info command handler, if specified.
160        $(
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            // We use a statically sized buffer to avoid allocating.
202            // This is needed since we use a custom allocator that relies on the Redis allocator,
203            // which isn't yet ready at this point.
204            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}