1#![allow(dead_code)]
4
5extern crate enum_primitive_derive;
6extern crate libc;
7extern crate num_traits;
8
9use std::cmp::Ordering;
10use std::ffi::{c_ulonglong, CStr, CString};
11use std::os::raw::{c_char, c_double, c_int, c_long, c_longlong, c_void};
12use std::ptr;
13use std::slice;
14
15use crate::RedisResult;
16use bitflags::bitflags;
17use enum_primitive_derive::Primitive;
18use libc::size_t;
19use num_traits::FromPrimitive;
20
21use crate::error::Error;
22pub use crate::redisraw::bindings::*;
23use crate::{context::StrCallArgs, Context, RedisString};
24use crate::{RedisBuffer, RedisError};
25
26const GENERIC_ERROR_MESSAGE: &str = "Generic error.";
27
28bitflags! {
29 pub struct KeyMode: c_int {
30 const READ = REDISMODULE_READ as c_int;
31 const WRITE = REDISMODULE_WRITE as c_int;
32 }
33}
34
35bitflags! {
36 pub struct ModuleOptions: c_int {
37 const HANDLE_IO_ERRORS = REDISMODULE_OPTIONS_HANDLE_IO_ERRORS as c_int;
38 const NO_IMPLICIT_SIGNAL_MODIFIED = REDISMODULE_OPTION_NO_IMPLICIT_SIGNAL_MODIFIED as c_int;
39 const HANDLE_REPL_ASYNC_LOAD = REDISMODULE_OPTIONS_HANDLE_REPL_ASYNC_LOAD as c_int;
40 }
41}
42
43#[derive(Primitive, Debug, PartialEq, Eq)]
44pub enum KeyType {
45 Empty = REDISMODULE_KEYTYPE_EMPTY,
46 String = REDISMODULE_KEYTYPE_STRING,
47 List = REDISMODULE_KEYTYPE_LIST,
48 Hash = REDISMODULE_KEYTYPE_HASH,
49 Set = REDISMODULE_KEYTYPE_SET,
50 ZSet = REDISMODULE_KEYTYPE_ZSET,
51 Module = REDISMODULE_KEYTYPE_MODULE,
52 Stream = REDISMODULE_KEYTYPE_STREAM,
53}
54
55impl From<c_int> for KeyType {
56 fn from(v: c_int) -> Self {
57 Self::from_i32(v).unwrap()
58 }
59}
60
61#[derive(Primitive, Debug, PartialEq, Eq)]
62pub enum Where {
63 ListHead = REDISMODULE_LIST_HEAD,
64 ListTail = REDISMODULE_LIST_TAIL,
65}
66
67#[derive(Primitive, Debug, PartialEq, Eq)]
68pub enum ReplyType {
69 Unknown = REDISMODULE_REPLY_UNKNOWN,
70 String = REDISMODULE_REPLY_STRING,
71 Error = REDISMODULE_REPLY_ERROR,
72 Integer = REDISMODULE_REPLY_INTEGER,
73 Array = REDISMODULE_REPLY_ARRAY,
74 Null = REDISMODULE_REPLY_NULL,
75 Map = REDISMODULE_REPLY_MAP,
76 Set = REDISMODULE_REPLY_SET,
77 Bool = REDISMODULE_REPLY_BOOL,
78 Double = REDISMODULE_REPLY_DOUBLE,
79 BigNumber = REDISMODULE_REPLY_BIG_NUMBER,
80 VerbatimString = REDISMODULE_REPLY_VERBATIM_STRING,
81}
82
83impl From<c_int> for ReplyType {
84 fn from(v: c_int) -> Self {
85 Self::from_i32(v).unwrap()
86 }
87}
88
89#[derive(Primitive, Debug, PartialEq, Eq)]
90pub enum Aux {
91 Before = REDISMODULE_AUX_BEFORE_RDB,
92 After = REDISMODULE_AUX_AFTER_RDB,
93}
94
95#[derive(Primitive, Debug, PartialEq, Eq)]
96pub enum Status {
97 Ok = REDISMODULE_OK,
98 Err = REDISMODULE_ERR,
99}
100
101impl From<Status> for RedisResult<()> {
102 fn from(value: Status) -> Self {
103 match value {
104 Status::Ok => Ok(()),
105 Status::Err => Err(RedisError::Str(GENERIC_ERROR_MESSAGE)),
106 }
107 }
108}
109
110impl From<c_int> for Status {
111 fn from(v: c_int) -> Self {
112 Self::from_i32(v).unwrap()
113 }
114}
115
116impl From<Status> for Result<(), &str> {
117 fn from(s: Status) -> Self {
118 match s {
119 Status::Ok => Ok(()),
120 Status::Err => Err(GENERIC_ERROR_MESSAGE),
121 }
122 }
123}
124
125bitflags! {
126 #[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
127 pub struct NotifyEvent : c_int {
128 const KEYSPACE = REDISMODULE_NOTIFY_KEYSPACE;
129 const KEYEVENT = REDISMODULE_NOTIFY_KEYEVENT;
130 const GENERIC = REDISMODULE_NOTIFY_GENERIC;
131 const STRING = REDISMODULE_NOTIFY_STRING;
132 const LIST = REDISMODULE_NOTIFY_LIST;
133 const SET = REDISMODULE_NOTIFY_SET;
134 const HASH = REDISMODULE_NOTIFY_HASH;
135 const ZSET = REDISMODULE_NOTIFY_ZSET;
136 const EXPIRED = REDISMODULE_NOTIFY_EXPIRED;
137 const EVICTED = REDISMODULE_NOTIFY_EVICTED;
138 const STREAM = REDISMODULE_NOTIFY_STREAM;
139 const NEW = REDISMODULE_NOTIFY_NEW;
141 const MODULE = REDISMODULE_NOTIFY_MODULE;
142 const LOADED = REDISMODULE_NOTIFY_LOADED;
143 const MISSED = REDISMODULE_NOTIFY_KEY_MISS;
144 const ALL = REDISMODULE_NOTIFY_ALL;
150 const TRIMMED = REDISMODULE_NOTIFY_TRIMMED;
151 }
152}
153
154#[derive(Debug)]
155pub enum CommandFlag {
156 Write,
157 Readonly,
158 Denyoom,
159 Admin,
160 Pubsub,
161 Noscript,
162 Random,
163 SortForScript,
164 Loading,
165 Stale,
166 SkipMonitor,
167 Asking,
168 Fast,
169 Movablekeys,
170}
171
172const fn command_flag_repr(flag: &CommandFlag) -> &'static str {
173 use crate::raw::CommandFlag::*;
174 match flag {
175 Write => "write",
176 Readonly => "readonly",
177 Denyoom => "denyoom",
178 Admin => "admin",
179 Pubsub => "pubsub",
180 Noscript => "noscript",
181 Random => "random",
182 SortForScript => "sort_for_script",
183 Loading => "loading",
184 Stale => "stale",
185 SkipMonitor => "skip_monitor",
186 Asking => "asking",
187 Fast => "fast",
188 Movablekeys => "movablekeys",
189 }
190}
191
192#[allow(improper_ctypes)]
195#[link(name = "redismodule", kind = "static")]
196extern "C" {
197 pub fn Export_RedisModule_Init(
198 ctx: *mut RedisModuleCtx,
199 module_name: *const c_char,
200 module_version: c_int,
201 api_version: c_int,
202 ) -> c_int;
203
204 pub fn Export_RedisModule_InitAPI(ctx: *mut RedisModuleCtx) -> c_void;
205}
206
207pub const FMT: *const c_char = c"v".as_ptr().cast::<c_char>();
210
211pub const REDISMODULE_HASH_DELETE: *const RedisModuleString =
215 std::ptr::dangling::<RedisModuleString>();
216
217#[allow(clippy::not_unsafe_ptr_arg_deref)]
220pub fn call_reply_type(reply: *mut RedisModuleCallReply) -> ReplyType {
221 unsafe {
222 RedisModule_CallReplyType.unwrap()(reply).into()
224 }
225}
226
227#[allow(clippy::not_unsafe_ptr_arg_deref)]
228pub fn free_call_reply(reply: *mut RedisModuleCallReply) {
229 unsafe { RedisModule_FreeCallReply.unwrap()(reply) }
230}
231
232#[allow(clippy::not_unsafe_ptr_arg_deref)]
233pub fn call_reply_integer(reply: *mut RedisModuleCallReply) -> c_longlong {
234 unsafe { RedisModule_CallReplyInteger.unwrap()(reply) }
235}
236
237#[allow(clippy::not_unsafe_ptr_arg_deref)]
241pub fn call_reply_bool(reply: *mut RedisModuleCallReply) -> bool {
242 (unsafe { RedisModule_CallReplyBool.unwrap()(reply) } != 0)
243}
244
245#[allow(clippy::not_unsafe_ptr_arg_deref)]
249pub fn call_reply_double(reply: *mut RedisModuleCallReply) -> f64 {
250 unsafe { RedisModule_CallReplyDouble.unwrap()(reply) }
251}
252
253#[allow(clippy::not_unsafe_ptr_arg_deref)]
257pub fn call_reply_big_number(reply: *mut RedisModuleCallReply) -> Option<String> {
258 unsafe {
259 let mut len: size_t = 0;
260 let reply_string: *mut u8 =
261 RedisModule_CallReplyBigNumber.unwrap()(reply, &mut len) as *mut u8;
262 if reply_string.is_null() {
263 return None;
264 }
265 String::from_utf8(slice::from_raw_parts(reply_string, len).to_vec()).ok()
266 }
267}
268
269#[allow(clippy::not_unsafe_ptr_arg_deref, invalid_null_arguments)]
273pub fn call_reply_verbatim_string(reply: *mut RedisModuleCallReply) -> Option<(String, Vec<u8>)> {
274 unsafe {
275 let mut len: size_t = 0;
276 let format: *const u8 = ptr::null();
277 let reply_string: *mut u8 =
278 RedisModule_CallReplyVerbatim.unwrap()(reply, &mut len, &mut (format as *const c_char))
279 as *mut u8;
280 if reply_string.is_null() {
281 return None;
282 }
283 Some((
284 String::from_utf8(slice::from_raw_parts(format, 3).to_vec()).ok()?,
285 slice::from_raw_parts(reply_string, len).to_vec(),
286 ))
287 }
288}
289
290#[allow(clippy::not_unsafe_ptr_arg_deref)]
291pub fn call_reply_array_element(
292 reply: *mut RedisModuleCallReply,
293 idx: usize,
294) -> *mut RedisModuleCallReply {
295 unsafe { RedisModule_CallReplyArrayElement.unwrap()(reply, idx) }
296}
297
298#[allow(clippy::not_unsafe_ptr_arg_deref)]
302pub fn call_reply_set_element(
303 reply: *mut RedisModuleCallReply,
304 idx: usize,
305) -> *mut RedisModuleCallReply {
306 unsafe { RedisModule_CallReplySetElement.unwrap()(reply, idx) }
307}
308
309#[allow(clippy::not_unsafe_ptr_arg_deref)]
313pub fn call_reply_map_element(
314 reply: *mut RedisModuleCallReply,
315 idx: usize,
316) -> (*mut RedisModuleCallReply, *mut RedisModuleCallReply) {
317 let mut key: *mut RedisModuleCallReply = ptr::null_mut();
318 let mut val: *mut RedisModuleCallReply = ptr::null_mut();
319 unsafe { RedisModule_CallReplyMapElement.unwrap()(reply, idx, &mut key, &mut val) };
320 (key, val)
321}
322
323#[allow(clippy::not_unsafe_ptr_arg_deref)]
324pub fn call_reply_length(reply: *mut RedisModuleCallReply) -> usize {
325 unsafe { RedisModule_CallReplyLength.unwrap()(reply) }
326}
327
328#[allow(clippy::not_unsafe_ptr_arg_deref)]
329pub fn call_reply_string_ptr(reply: *mut RedisModuleCallReply, len: *mut size_t) -> *const c_char {
330 unsafe { RedisModule_CallReplyStringPtr.unwrap()(reply, len) }
331}
332
333#[allow(clippy::not_unsafe_ptr_arg_deref)]
334pub fn call_reply_string(reply: *mut RedisModuleCallReply) -> Option<String> {
335 unsafe {
336 let mut len: size_t = 0;
337 let reply_string: *mut u8 =
338 RedisModule_CallReplyStringPtr.unwrap()(reply, &mut len) as *mut u8;
339 if reply_string.is_null() {
340 return None;
341 }
342 String::from_utf8(slice::from_raw_parts(reply_string, len).to_vec()).ok()
343 }
344}
345
346#[allow(clippy::not_unsafe_ptr_arg_deref)]
347#[inline]
348pub fn close_key(kp: *mut RedisModuleKey) {
349 unsafe { RedisModule_CloseKey.unwrap()(kp) }
350}
351
352#[allow(clippy::not_unsafe_ptr_arg_deref)]
353#[inline]
354pub fn open_key(
355 ctx: *mut RedisModuleCtx,
356 keyname: *mut RedisModuleString,
357 mode: KeyMode,
358) -> *mut RedisModuleKey {
359 unsafe { RedisModule_OpenKey.unwrap()(ctx, keyname, mode.bits()).cast::<RedisModuleKey>() }
360}
361
362#[allow(clippy::not_unsafe_ptr_arg_deref)]
363#[inline]
364pub(crate) fn open_key_with_flags(
365 ctx: *mut RedisModuleCtx,
366 keyname: *mut RedisModuleString,
367 mode: KeyMode,
368 flags: c_int,
369) -> *mut RedisModuleKey {
370 unsafe {
371 RedisModule_OpenKey.unwrap()(ctx, keyname, mode.bits() | flags).cast::<RedisModuleKey>()
372 }
373}
374
375#[allow(clippy::not_unsafe_ptr_arg_deref)]
376#[inline]
377pub fn reply_with_array(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
378 unsafe { RedisModule_ReplyWithArray.unwrap()(ctx, len).into() }
379}
380
381#[allow(clippy::not_unsafe_ptr_arg_deref)]
382#[inline]
383pub fn reply_with_map(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
384 unsafe {
385 RedisModule_ReplyWithMap
386 .map_or_else(
387 || RedisModule_ReplyWithArray.unwrap()(ctx, len * 2),
388 |f| f(ctx, len),
389 )
390 .into()
391 }
392}
393
394#[allow(clippy::not_unsafe_ptr_arg_deref)]
395#[inline]
396pub fn reply_with_set(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
397 unsafe {
398 RedisModule_ReplyWithSet
399 .map_or_else(
400 || RedisModule_ReplyWithArray.unwrap()(ctx, len * 2),
401 |f| f(ctx, len),
402 )
403 .into()
404 }
405}
406
407#[allow(clippy::not_unsafe_ptr_arg_deref)]
408#[inline]
409pub fn reply_with_attribute(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
410 unsafe { RedisModule_ReplyWithAttribute.unwrap()(ctx, len).into() }
411}
412
413#[allow(clippy::not_unsafe_ptr_arg_deref)]
414pub fn reply_with_error(ctx: *mut RedisModuleCtx, err: *const c_char) {
415 unsafe {
416 let msg = Context::str_as_legal_resp_string(CStr::from_ptr(err).to_str().unwrap());
417 RedisModule_ReplyWithError.unwrap()(ctx, msg.as_ptr());
418 }
419}
420
421#[allow(clippy::not_unsafe_ptr_arg_deref)]
422#[inline]
423pub fn reply_with_null(ctx: *mut RedisModuleCtx) -> Status {
424 unsafe { RedisModule_ReplyWithNull.unwrap()(ctx).into() }
425}
426
427#[allow(clippy::not_unsafe_ptr_arg_deref)]
428#[inline]
429pub fn reply_with_bool(ctx: *mut RedisModuleCtx, b: c_int) -> Status {
430 unsafe { RedisModule_ReplyWithBool.unwrap()(ctx, b).into() }
431}
432
433#[allow(clippy::not_unsafe_ptr_arg_deref)]
434#[inline]
435pub fn reply_with_long_long(ctx: *mut RedisModuleCtx, ll: c_longlong) -> Status {
436 unsafe { RedisModule_ReplyWithLongLong.unwrap()(ctx, ll).into() }
437}
438
439#[allow(clippy::not_unsafe_ptr_arg_deref)]
440#[inline]
441pub fn reply_with_double(ctx: *mut RedisModuleCtx, f: c_double) -> Status {
442 unsafe { RedisModule_ReplyWithDouble.unwrap()(ctx, f).into() }
443}
444
445#[allow(clippy::not_unsafe_ptr_arg_deref)]
446#[inline]
447pub fn reply_with_string(ctx: *mut RedisModuleCtx, s: *mut RedisModuleString) -> Status {
448 unsafe { RedisModule_ReplyWithString.unwrap()(ctx, s).into() }
449}
450
451#[allow(clippy::not_unsafe_ptr_arg_deref)]
452#[inline]
453pub fn reply_with_simple_string(ctx: *mut RedisModuleCtx, s: *const c_char) -> Status {
454 unsafe { RedisModule_ReplyWithSimpleString.unwrap()(ctx, s).into() }
455}
456
457#[allow(clippy::not_unsafe_ptr_arg_deref)]
458#[inline]
459pub fn reply_with_string_buffer(ctx: *mut RedisModuleCtx, s: *const c_char, len: size_t) -> Status {
460 unsafe { RedisModule_ReplyWithStringBuffer.unwrap()(ctx, s, len).into() }
461}
462
463#[allow(clippy::not_unsafe_ptr_arg_deref)]
464#[inline]
465pub fn reply_with_big_number(ctx: *mut RedisModuleCtx, s: *const c_char, len: size_t) -> Status {
466 unsafe { RedisModule_ReplyWithBigNumber.unwrap()(ctx, s, len).into() }
467}
468
469#[allow(clippy::not_unsafe_ptr_arg_deref)]
470#[inline]
471pub fn reply_with_verbatim_string(
472 ctx: *mut RedisModuleCtx,
473 s: *const c_char,
474 len: size_t,
475 format: *const c_char,
476) -> Status {
477 unsafe { RedisModule_ReplyWithVerbatimStringType.unwrap()(ctx, s, len, format).into() }
478}
479
480#[allow(clippy::not_unsafe_ptr_arg_deref)]
484#[inline]
485pub fn set_expire(key: *mut RedisModuleKey, expire: c_longlong) -> Status {
486 unsafe { RedisModule_SetExpire.unwrap()(key, expire).into() }
487}
488
489#[allow(clippy::not_unsafe_ptr_arg_deref)]
490#[inline]
491pub fn string_dma(key: *mut RedisModuleKey, len: *mut size_t, mode: KeyMode) -> *mut c_char {
492 unsafe { RedisModule_StringDMA.unwrap()(key, len, mode.bits()) }
493}
494
495#[allow(clippy::not_unsafe_ptr_arg_deref)]
496#[inline]
497pub fn string_truncate(key: *mut RedisModuleKey, new_len: size_t) -> Status {
498 unsafe { RedisModule_StringTruncate.unwrap()(key, new_len).into() }
499}
500
501#[allow(clippy::not_unsafe_ptr_arg_deref)]
502pub fn hash_get_multi<T>(
503 key: *mut RedisModuleKey,
504 fields: &[T],
505 values: &mut [*mut RedisModuleString],
506) -> Result<(), RedisError>
507where
508 T: Into<Vec<u8>> + Clone,
509{
510 assert_eq!(fields.len(), values.len());
511
512 let fields = fields
513 .iter()
514 .map(|e| CString::new(e.clone()))
515 .collect::<Result<Vec<CString>, _>>()?;
516
517 let mut fi = fields.iter();
518 let mut vi = values.iter_mut();
519
520 macro_rules! rm {
521 () => { unsafe {
522 RedisModule_HashGet.unwrap()(key, REDISMODULE_HASH_CFIELDS as i32,
523 ptr::null::<c_char>())
524 }};
525 ($($args:expr)*) => { unsafe {
526 RedisModule_HashGet.unwrap()(
527 key, REDISMODULE_HASH_CFIELDS as i32,
528 $($args),*,
529 ptr::null::<c_char>()
530 )
531 }};
532 }
533 macro_rules! f {
534 () => {
535 fi.next().unwrap().as_ptr()
536 };
537 }
538 macro_rules! v {
539 () => {
540 vi.next().unwrap()
541 };
542 }
543
544 let res = Status::from(match fields.len() {
549 0 => rm! {},
550 1 => rm! {f!() v!()},
551 2 => rm! {f!() v!() f!() v!()},
552 3 => rm! {f!() v!() f!() v!() f!() v!()},
553 4 => rm! {f!() v!() f!() v!() f!() v!() f!() v!()},
554 5 => rm! {f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()},
555 6 => rm! {f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()},
556 7 => rm! {
557 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
558 f!() v!()
559 },
560 8 => rm! {
561 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
562 f!() v!() f!() v!()
563 },
564 9 => rm! {
565 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
566 f!() v!() f!() v!() f!() v!()
567 },
568 10 => rm! {
569 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
570 f!() v!() f!() v!() f!() v!() f!() v!()
571 },
572 11 => rm! {
573 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
574 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
575 },
576 12 => rm! {
577 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
578 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
579 },
580 _ => panic!("Unsupported length"),
581 });
582
583 match res {
584 Status::Ok => Ok(()),
585 Status::Err => Err(RedisError::Str("ERR key is not a hash value")),
586 }
587}
588
589#[allow(clippy::not_unsafe_ptr_arg_deref)]
590#[inline]
591pub fn hash_set(key: *mut RedisModuleKey, field: &str, value: *mut RedisModuleString) -> Status {
592 let field = CString::new(field).unwrap();
593
594 unsafe {
595 RedisModule_HashSet.unwrap()(
596 key,
597 REDISMODULE_HASH_CFIELDS as i32,
598 field.as_ptr(),
599 value,
600 ptr::null::<c_char>(),
601 )
602 .into()
603 }
604}
605
606#[allow(clippy::not_unsafe_ptr_arg_deref)]
607#[inline]
608pub fn hash_del(key: *mut RedisModuleKey, field: &str) -> Status {
609 let field = CString::new(field).unwrap();
610
611 unsafe {
616 RedisModule_HashSet.unwrap()(
617 key,
618 REDISMODULE_HASH_CFIELDS as i32,
619 field.as_ptr(),
620 REDISMODULE_HASH_DELETE,
621 ptr::null::<c_char>(),
622 )
623 .into()
624 }
625}
626
627#[allow(clippy::not_unsafe_ptr_arg_deref)]
628#[inline]
629pub fn list_push(
630 key: *mut RedisModuleKey,
631 list_where: Where,
632 element: *mut RedisModuleString,
633) -> Status {
634 unsafe { RedisModule_ListPush.unwrap()(key, list_where as i32, element).into() }
635}
636
637#[allow(clippy::not_unsafe_ptr_arg_deref)]
638#[inline]
639pub fn list_pop(key: *mut RedisModuleKey, list_where: Where) -> *mut RedisModuleString {
640 unsafe { RedisModule_ListPop.unwrap()(key, list_where as i32) }
641}
642
643#[allow(clippy::not_unsafe_ptr_arg_deref)]
645#[inline]
646pub fn string_ptr_len(s: *const RedisModuleString, len: *mut size_t) -> *const c_char {
647 unsafe { RedisModule_StringPtrLen.unwrap()(s, len) }
648}
649
650#[allow(clippy::not_unsafe_ptr_arg_deref)]
651#[inline]
652pub fn string_retain_string(ctx: *mut RedisModuleCtx, s: *mut RedisModuleString) {
653 unsafe { RedisModule_RetainString.unwrap()(ctx, s) }
654}
655
656#[allow(clippy::not_unsafe_ptr_arg_deref)]
657#[inline]
658pub fn string_to_longlong(s: *const RedisModuleString, len: *mut i64) -> Status {
659 unsafe { RedisModule_StringToLongLong.unwrap()(s, len).into() }
660}
661
662#[allow(clippy::not_unsafe_ptr_arg_deref)]
663#[inline]
664pub fn string_to_double(s: *const RedisModuleString, len: *mut f64) -> Status {
665 unsafe { RedisModule_StringToDouble.unwrap()(s, len).into() }
666}
667
668#[allow(clippy::not_unsafe_ptr_arg_deref)]
669#[inline]
670pub fn string_set(key: *mut RedisModuleKey, s: *mut RedisModuleString) -> Status {
671 unsafe { RedisModule_StringSet.unwrap()(key, s).into() }
672}
673
674#[allow(clippy::not_unsafe_ptr_arg_deref)]
675#[inline]
676pub fn replicate_verbatim(ctx: *mut RedisModuleCtx) -> Status {
677 unsafe { RedisModule_ReplicateVerbatim.unwrap()(ctx).into() }
678}
679
680fn load<F, T>(rdb: *mut RedisModuleIO, f: F) -> Result<T, Error>
681where
682 F: FnOnce(*mut RedisModuleIO) -> T,
683{
684 let res = f(rdb);
685 if is_io_error(rdb) {
686 Err(RedisError::short_read().into())
687 } else {
688 Ok(res)
689 }
690}
691
692#[allow(clippy::not_unsafe_ptr_arg_deref)]
693pub fn load_unsigned(rdb: *mut RedisModuleIO) -> Result<u64, Error> {
694 unsafe { load(rdb, |rdb| RedisModule_LoadUnsigned.unwrap()(rdb)) }
695}
696
697#[allow(clippy::not_unsafe_ptr_arg_deref)]
698pub fn load_signed(rdb: *mut RedisModuleIO) -> Result<i64, Error> {
699 unsafe { load(rdb, |rdb| RedisModule_LoadSigned.unwrap()(rdb)) }
700}
701
702#[allow(clippy::not_unsafe_ptr_arg_deref)]
703pub fn load_string(rdb: *mut RedisModuleIO) -> Result<RedisString, Error> {
704 let p = unsafe { load(rdb, |rdb| RedisModule_LoadString.unwrap()(rdb))? };
705 Ok(RedisString::from_redis_module_string(ptr::null_mut(), p))
706}
707
708#[allow(clippy::not_unsafe_ptr_arg_deref)]
709pub fn load_string_buffer(rdb: *mut RedisModuleIO) -> Result<RedisBuffer, Error> {
710 unsafe {
711 let mut len = 0;
712 let buffer = load(rdb, |rdb| {
713 RedisModule_LoadStringBuffer.unwrap()(rdb, &mut len)
714 })?;
715 Ok(RedisBuffer::new(buffer, len))
716 }
717}
718
719#[allow(clippy::not_unsafe_ptr_arg_deref)]
720pub fn replicate<'a, T: Into<StrCallArgs<'a>>>(
721 ctx: *mut RedisModuleCtx,
722 command: &str,
723 args: T,
724) -> Status {
725 let mut call_args: StrCallArgs = args.into();
726 let final_args = call_args.args_mut();
727
728 let cmd = CString::new(command).unwrap();
729
730 unsafe {
731 RedisModule_Replicate.unwrap()(
732 ctx,
733 cmd.as_ptr(),
734 FMT,
735 final_args.as_ptr(),
736 final_args.len(),
737 )
738 .into()
739 }
740}
741
742#[allow(clippy::not_unsafe_ptr_arg_deref)]
743pub fn load_double(rdb: *mut RedisModuleIO) -> Result<f64, Error> {
744 unsafe { load(rdb, |rdb| RedisModule_LoadDouble.unwrap()(rdb)) }
745}
746
747#[allow(clippy::not_unsafe_ptr_arg_deref)]
748pub fn load_float(rdb: *mut RedisModuleIO) -> Result<f32, Error> {
749 unsafe { load(rdb, |rdb| RedisModule_LoadFloat.unwrap()(rdb)) }
750}
751
752#[allow(clippy::not_unsafe_ptr_arg_deref)]
753pub fn save_string(rdb: *mut RedisModuleIO, buf: &str) {
754 unsafe { RedisModule_SaveStringBuffer.unwrap()(rdb, buf.as_ptr().cast::<c_char>(), buf.len()) };
755}
756
757#[allow(clippy::not_unsafe_ptr_arg_deref)]
758pub fn save_redis_string(rdb: *mut RedisModuleIO, s: &RedisString) {
760 unsafe { RedisModule_SaveString.unwrap()(rdb, s.inner) };
761}
762
763#[allow(clippy::not_unsafe_ptr_arg_deref)]
764pub fn save_slice(rdb: *mut RedisModuleIO, buf: &[u8]) {
766 unsafe { RedisModule_SaveStringBuffer.unwrap()(rdb, buf.as_ptr().cast::<c_char>(), buf.len()) };
767}
768
769#[allow(clippy::not_unsafe_ptr_arg_deref)]
770pub fn save_double(rdb: *mut RedisModuleIO, val: f64) {
771 unsafe { RedisModule_SaveDouble.unwrap()(rdb, val) };
772}
773
774#[allow(clippy::not_unsafe_ptr_arg_deref)]
775pub fn save_signed(rdb: *mut RedisModuleIO, val: i64) {
776 unsafe { RedisModule_SaveSigned.unwrap()(rdb, val) };
777}
778
779#[allow(clippy::not_unsafe_ptr_arg_deref)]
780pub fn save_float(rdb: *mut RedisModuleIO, val: f32) {
781 unsafe { RedisModule_SaveFloat.unwrap()(rdb, val) };
782}
783
784#[allow(clippy::not_unsafe_ptr_arg_deref)]
785pub fn save_unsigned(rdb: *mut RedisModuleIO, val: u64) {
786 unsafe { RedisModule_SaveUnsigned.unwrap()(rdb, val) };
787}
788
789#[allow(clippy::not_unsafe_ptr_arg_deref)]
790pub fn string_compare(a: *mut RedisModuleString, b: *mut RedisModuleString) -> Ordering {
791 unsafe { RedisModule_StringCompare.unwrap()(a, b).cmp(&0) }
792}
793
794#[allow(clippy::not_unsafe_ptr_arg_deref)]
795pub fn string_append_buffer(
796 ctx: *mut RedisModuleCtx,
797 s: *mut RedisModuleString,
798 buff: &str,
799) -> Status {
800 unsafe {
801 RedisModule_StringAppendBuffer.unwrap()(ctx, s, buff.as_ptr().cast::<c_char>(), buff.len())
802 .into()
803 }
804}
805
806#[allow(clippy::not_unsafe_ptr_arg_deref)]
807pub fn subscribe_to_server_event(
808 ctx: *mut RedisModuleCtx,
809 event: RedisModuleEvent,
810 callback: RedisModuleEventCallback,
811) -> Status {
812 unsafe { RedisModule_SubscribeToServerEvent.unwrap()(ctx, event, callback).into() }
813}
814
815#[allow(clippy::not_unsafe_ptr_arg_deref)]
816pub fn register_info_function(ctx: *mut RedisModuleCtx, callback: RedisModuleInfoFunc) -> Status {
817 unsafe { RedisModule_RegisterInfoFunc.unwrap()(ctx, callback).into() }
818}
819
820#[allow(clippy::not_unsafe_ptr_arg_deref)]
821pub fn add_info_section(ctx: *mut RedisModuleInfoCtx, name: Option<&str>) -> Status {
822 name.map(|n| CString::new(n).unwrap()).map_or_else(
823 || unsafe { RedisModule_InfoAddSection.unwrap()(ctx, ptr::null_mut()).into() },
824 |n| unsafe { RedisModule_InfoAddSection.unwrap()(ctx, n.as_ptr()).into() },
825 )
826}
827
828#[allow(clippy::not_unsafe_ptr_arg_deref)]
829pub fn add_info_field_str(ctx: *mut RedisModuleInfoCtx, name: &str, content: &str) -> Status {
830 let name = CString::new(name).unwrap();
831 let content = RedisString::create(None, content);
832 unsafe { RedisModule_InfoAddFieldString.unwrap()(ctx, name.as_ptr(), content.inner).into() }
833}
834
835#[allow(clippy::not_unsafe_ptr_arg_deref)]
836pub fn add_info_field_long_long(
837 ctx: *mut RedisModuleInfoCtx,
838 name: &str,
839 value: c_longlong,
840) -> Status {
841 let name = CString::new(name).unwrap();
842 unsafe { RedisModule_InfoAddFieldLongLong.unwrap()(ctx, name.as_ptr(), value).into() }
843}
844
845#[allow(clippy::not_unsafe_ptr_arg_deref)]
846pub fn add_info_field_unsigned_long_long(
847 ctx: *mut RedisModuleInfoCtx,
848 name: &str,
849 value: c_ulonglong,
850) -> Status {
851 let name = CString::new(name).unwrap();
852 unsafe { RedisModule_InfoAddFieldULongLong.unwrap()(ctx, name.as_ptr(), value).into() }
853}
854
855#[allow(clippy::not_unsafe_ptr_arg_deref)]
856pub fn add_info_field_double(ctx: *mut RedisModuleInfoCtx, name: &str, value: c_double) -> Status {
857 let name = CString::new(name).unwrap();
858 unsafe { RedisModule_InfoAddFieldDouble.unwrap()(ctx, name.as_ptr(), value).into() }
859}
860
861#[allow(clippy::not_unsafe_ptr_arg_deref)]
862pub fn add_info_begin_dict_field(ctx: *mut RedisModuleInfoCtx, name: &str) -> Status {
863 let name = CString::new(name).unwrap();
864 unsafe { RedisModule_InfoBeginDictField.unwrap()(ctx, name.as_ptr()).into() }
865}
866
867#[allow(clippy::not_unsafe_ptr_arg_deref)]
868pub fn add_info_end_dict_field(ctx: *mut RedisModuleInfoCtx) -> Status {
869 unsafe { RedisModule_InfoEndDictField.unwrap()(ctx).into() }
870}
871
872pub unsafe fn export_shared_api(
881 ctx: *mut RedisModuleCtx,
882 func: *const ::std::os::raw::c_void,
883 name: *const ::std::os::raw::c_char,
884) {
885 RedisModule_ExportSharedAPI.unwrap()(ctx, name, func as *mut ::std::os::raw::c_void);
886}
887
888pub unsafe fn notify_keyspace_event(
897 ctx: *mut RedisModuleCtx,
898 event_type: NotifyEvent,
899 event: &str,
900 keyname: &RedisString,
901) -> Status {
902 let event = CString::new(event).unwrap();
903 RedisModule_NotifyKeyspaceEvent.unwrap()(ctx, event_type.bits(), event.as_ptr(), keyname.inner)
904 .into()
905}
906
907#[must_use]
911pub fn get_keyspace_events() -> NotifyEvent {
912 unsafe {
913 let events = RedisModule_GetNotifyKeyspaceEvents.unwrap()();
914 NotifyEvent::from_bits_truncate(events)
915 }
916}
917
918pub fn get_keyspace_notification_flags_all() -> NotifyEvent {
931 unsafe {
932 NotifyEvent::from_bits_truncate(RedisModule_GetKeyspaceNotificationFlagsAll.unwrap()())
933 }
934}
935
936#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
937pub struct Version {
938 pub major: i32,
939 pub minor: i32,
940 pub patch: i32,
941}
942
943impl From<c_int> for Version {
944 fn from(ver: c_int) -> Self {
945 Self {
947 major: (ver & 0x00FF_0000) >> 16,
948 minor: (ver & 0x0000_FF00) >> 8,
949 patch: (ver & 0x0000_00FF),
950 }
951 }
952}
953
954#[allow(clippy::not_unsafe_ptr_arg_deref)]
955pub fn is_io_error(rdb: *mut RedisModuleIO) -> bool {
956 unsafe { RedisModule_IsIOError.unwrap()(rdb) != 0 }
957}
958
959#[allow(clippy::not_unsafe_ptr_arg_deref)]
960pub fn redis_log(ctx: *mut RedisModuleCtx, msg: &str) {
961 let level = CString::new("notice").unwrap(); let msg = CString::new(msg).unwrap();
963 unsafe {
964 RedisModule_Log.unwrap()(ctx, level.as_ptr(), msg.as_ptr());
965 }
966}