1 #[macro_export]
2 macro_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) 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 #[cfg(feature = "experimental-api")]
45 #[macro_export]
46 macro_rules! redis_event_handler {
47     (
48         $ctx: expr,
49         $event_type: expr,
50         $event_handler: expr
51     ) => {{
52         extern "C" fn __handle_event(
53             ctx: *mut $crate::raw::RedisModuleCtx,
54             event_type: c_int,
55             event: *const c_char,
56             key: *mut $crate::raw::RedisModuleString,
57         ) -> c_int {
58             let context = $crate::Context::new(ctx);
59 
60             let redis_key = $crate::RedisString::from_ptr(key).unwrap();
61             let event_str = unsafe { CStr::from_ptr(event) };
62             $event_handler(
63                 &context,
64                 $crate::NotifyEvent::from_bits_truncate(event_type),
65                 event_str.to_str().unwrap(),
66                 redis_key,
67             );
68 
69             $crate::raw::Status::Ok as c_int
70         }
71 
72         if unsafe {
73             $crate::raw::RedisModule_SubscribeToKeyspaceEvents.unwrap()(
74                 $ctx,
75                 $event_type.bits(),
76                 Some(__handle_event),
77             )
78         } == $crate::raw::Status::Err as c_int
79         {
80             return $crate::raw::Status::Err as c_int;
81         }
82     }};
83 }
84 
85 #[macro_export]
86 macro_rules! redis_module {
87     (
88         name: $module_name:expr,
89         version: $module_version:expr,
90         data_types: [
91             $($data_type:ident),* $(,)*
92         ],
93         $(init: $init_func:ident,)* $(,)*
94         $(deinit: $deinit_func:ident,)* $(,)*
95         commands: [
96             $([
97                 $name:expr,
98                 $command:expr,
99                 $flags:expr,
100                 $firstkey:expr,
101                 $lastkey:expr,
102                 $keystep:expr
103               ]),* $(,)*
104         ] $(,)*
105         $(event_handlers: [
106             $([
107                 $(@$event_type:ident) +:
108                 $event_handler:expr
109             ]),* $(,)*
110         ])?
111     ) => {
112         extern "C" fn __info_func(
113             ctx: *mut $crate::raw::RedisModuleInfoCtx,
114             for_crash_report: i32,
115         ) {
116             $crate::base_info_func(ctx, for_crash_report == 1);
117         }
118 
119         #[no_mangle]
120         #[allow(non_snake_case)]
121         pub extern "C" fn RedisModule_OnLoad(
122             ctx: *mut $crate::raw::RedisModuleCtx,
123             argv: *mut *mut $crate::raw::RedisModuleString,
124             argc: std::os::raw::c_int,
125         ) -> std::os::raw::c_int {
126             use std::os::raw::{c_int, c_char};
127             use std::ffi::{CString, CStr};
128 
129             use $crate::raw;
130             use $crate::RedisString;
131 
132             // We use a statically sized buffer to avoid allocating.
133             // This is needed since we use a custom allocator that relies on the Redis allocator,
134             // which isn't yet ready at this point.
135             let mut name_buffer = [0; 64];
136             unsafe {
137                 std::ptr::copy(
138                     $module_name.as_ptr(),
139                     name_buffer.as_mut_ptr(),
140                     $module_name.len(),
141                 );
142             }
143 
144             let module_version = $module_version as c_int;
145 
146             if unsafe { raw::Export_RedisModule_Init(
147                 ctx,
148                 name_buffer.as_ptr() as *const c_char,
149                 module_version,
150                 raw::REDISMODULE_APIVER_1 as c_int,
151             ) } == raw::Status::Err as c_int { return raw::Status::Err as c_int; }
152 
153             let context = $crate::Context::new(ctx);
154             let args = $crate::decode_args(ctx, argv, argc);
155 
156             $(
157                 if $init_func(&context, &args) == $crate::Status::Err {
158                     return $crate::Status::Err as c_int;
159                 }
160             )*
161 
162             $(
163                 if (&$data_type).create_data_type(ctx).is_err() {
164                     return raw::Status::Err as c_int;
165                 }
166             )*
167 
168             $(
169                 redis_command!(ctx, $name, $command, $flags, $firstkey, $lastkey, $keystep);
170             )*
171 
172             $(
173                 $(
174                     redis_event_handler!(ctx, $(raw::NotifyEvent::$event_type |)+ raw::NotifyEvent::empty(), $event_handler);
175                 )*
176             )?
177 
178             raw::register_info_function(ctx, Some(__info_func));
179 
180             raw::Status::Ok as c_int
181         }
182 
183         #[no_mangle]
184         #[allow(non_snake_case)]
185         pub extern "C" fn RedisModule_OnUnload(
186             ctx: *mut $crate::raw::RedisModuleCtx
187         ) -> std::os::raw::c_int {
188             use std::os::raw::c_int;
189 
190             let context = $crate::Context::new(ctx);
191             $(
192                 if $deinit_func(&context) == $crate::Status::Err {
193                     return $crate::Status::Err as c_int;
194                 }
195             )*
196 
197             $crate::raw::Status::Ok as c_int
198         }
199     }
200 }
201