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 GENERIC = REDISMODULE_NOTIFY_GENERIC;
129 const STRING = REDISMODULE_NOTIFY_STRING;
130 const LIST = REDISMODULE_NOTIFY_LIST;
131 const SET = REDISMODULE_NOTIFY_SET;
132 const HASH = REDISMODULE_NOTIFY_HASH;
133 const ZSET = REDISMODULE_NOTIFY_ZSET;
134 const EXPIRED = REDISMODULE_NOTIFY_EXPIRED;
135 const EVICTED = REDISMODULE_NOTIFY_EVICTED;
136 const STREAM = REDISMODULE_NOTIFY_STREAM;
137 const MODULE = REDISMODULE_NOTIFY_MODULE;
138 const LOADED = REDISMODULE_NOTIFY_LOADED;
139 const MISSED = REDISMODULE_NOTIFY_KEY_MISS;
140 const ALL = REDISMODULE_NOTIFY_ALL;
141 const TRIMMED = REDISMODULE_NOTIFY_TRIMMED;
142 }
143}
144
145#[derive(Debug)]
146pub enum CommandFlag {
147 Write,
148 Readonly,
149 Denyoom,
150 Admin,
151 Pubsub,
152 Noscript,
153 Random,
154 SortForScript,
155 Loading,
156 Stale,
157 SkipMonitor,
158 Asking,
159 Fast,
160 Movablekeys,
161}
162
163const fn command_flag_repr(flag: &CommandFlag) -> &'static str {
164 use crate::raw::CommandFlag::*;
165 match flag {
166 Write => "write",
167 Readonly => "readonly",
168 Denyoom => "denyoom",
169 Admin => "admin",
170 Pubsub => "pubsub",
171 Noscript => "noscript",
172 Random => "random",
173 SortForScript => "sort_for_script",
174 Loading => "loading",
175 Stale => "stale",
176 SkipMonitor => "skip_monitor",
177 Asking => "asking",
178 Fast => "fast",
179 Movablekeys => "movablekeys",
180 }
181}
182
183#[allow(improper_ctypes)]
186#[link(name = "redismodule", kind = "static")]
187extern "C" {
188 pub fn Export_RedisModule_Init(
189 ctx: *mut RedisModuleCtx,
190 module_name: *const c_char,
191 module_version: c_int,
192 api_version: c_int,
193 ) -> c_int;
194
195 pub fn Export_RedisModule_InitAPI(ctx: *mut RedisModuleCtx) -> c_void;
196}
197
198pub const FMT: *const c_char = b"v\0".as_ptr().cast::<c_char>();
201
202pub const REDISMODULE_HASH_DELETE: *const RedisModuleString = 1 as *const RedisModuleString;
206
207#[allow(clippy::not_unsafe_ptr_arg_deref)]
210pub fn call_reply_type(reply: *mut RedisModuleCallReply) -> ReplyType {
211 unsafe {
212 RedisModule_CallReplyType.unwrap()(reply).into()
214 }
215}
216
217#[allow(clippy::not_unsafe_ptr_arg_deref)]
218pub fn free_call_reply(reply: *mut RedisModuleCallReply) {
219 unsafe { RedisModule_FreeCallReply.unwrap()(reply) }
220}
221
222#[allow(clippy::not_unsafe_ptr_arg_deref)]
223pub fn call_reply_integer(reply: *mut RedisModuleCallReply) -> c_longlong {
224 unsafe { RedisModule_CallReplyInteger.unwrap()(reply) }
225}
226
227#[allow(clippy::not_unsafe_ptr_arg_deref)]
231pub fn call_reply_bool(reply: *mut RedisModuleCallReply) -> bool {
232 (unsafe { RedisModule_CallReplyBool.unwrap()(reply) } != 0)
233}
234
235#[allow(clippy::not_unsafe_ptr_arg_deref)]
239pub fn call_reply_double(reply: *mut RedisModuleCallReply) -> f64 {
240 unsafe { RedisModule_CallReplyDouble.unwrap()(reply) }
241}
242
243#[allow(clippy::not_unsafe_ptr_arg_deref)]
247pub fn call_reply_big_number(reply: *mut RedisModuleCallReply) -> Option<String> {
248 unsafe {
249 let mut len: size_t = 0;
250 let reply_string: *mut u8 =
251 RedisModule_CallReplyBigNumber.unwrap()(reply, &mut len) as *mut u8;
252 if reply_string.is_null() {
253 return None;
254 }
255 String::from_utf8(slice::from_raw_parts(reply_string, len).to_vec()).ok()
256 }
257}
258
259#[allow(clippy::not_unsafe_ptr_arg_deref)]
263pub fn call_reply_verbatim_string(reply: *mut RedisModuleCallReply) -> Option<(String, Vec<u8>)> {
264 unsafe {
265 let mut len: size_t = 0;
266 let format: *const u8 = ptr::null();
267 let reply_string: *mut u8 =
268 RedisModule_CallReplyVerbatim.unwrap()(reply, &mut len, &mut (format as *const c_char))
269 as *mut u8;
270 if reply_string.is_null() {
271 return None;
272 }
273 Some((
274 String::from_utf8(slice::from_raw_parts(format, 3).to_vec()).ok()?,
275 slice::from_raw_parts(reply_string, len).to_vec(),
276 ))
277 }
278}
279
280#[allow(clippy::not_unsafe_ptr_arg_deref)]
281pub fn call_reply_array_element(
282 reply: *mut RedisModuleCallReply,
283 idx: usize,
284) -> *mut RedisModuleCallReply {
285 unsafe { RedisModule_CallReplyArrayElement.unwrap()(reply, idx) }
286}
287
288#[allow(clippy::not_unsafe_ptr_arg_deref)]
292pub fn call_reply_set_element(
293 reply: *mut RedisModuleCallReply,
294 idx: usize,
295) -> *mut RedisModuleCallReply {
296 unsafe { RedisModule_CallReplySetElement.unwrap()(reply, idx) }
297}
298
299#[allow(clippy::not_unsafe_ptr_arg_deref)]
303pub fn call_reply_map_element(
304 reply: *mut RedisModuleCallReply,
305 idx: usize,
306) -> (*mut RedisModuleCallReply, *mut RedisModuleCallReply) {
307 let mut key: *mut RedisModuleCallReply = ptr::null_mut();
308 let mut val: *mut RedisModuleCallReply = ptr::null_mut();
309 unsafe { RedisModule_CallReplyMapElement.unwrap()(reply, idx, &mut key, &mut val) };
310 (key, val)
311}
312
313#[allow(clippy::not_unsafe_ptr_arg_deref)]
314pub fn call_reply_length(reply: *mut RedisModuleCallReply) -> usize {
315 unsafe { RedisModule_CallReplyLength.unwrap()(reply) }
316}
317
318#[allow(clippy::not_unsafe_ptr_arg_deref)]
319pub fn call_reply_string_ptr(reply: *mut RedisModuleCallReply, len: *mut size_t) -> *const c_char {
320 unsafe { RedisModule_CallReplyStringPtr.unwrap()(reply, len) }
321}
322
323#[allow(clippy::not_unsafe_ptr_arg_deref)]
324pub fn call_reply_string(reply: *mut RedisModuleCallReply) -> Option<String> {
325 unsafe {
326 let mut len: size_t = 0;
327 let reply_string: *mut u8 =
328 RedisModule_CallReplyStringPtr.unwrap()(reply, &mut len) as *mut u8;
329 if reply_string.is_null() {
330 return None;
331 }
332 String::from_utf8(slice::from_raw_parts(reply_string, len).to_vec()).ok()
333 }
334}
335
336#[allow(clippy::not_unsafe_ptr_arg_deref)]
337#[inline]
338pub fn close_key(kp: *mut RedisModuleKey) {
339 unsafe { RedisModule_CloseKey.unwrap()(kp) }
340}
341
342#[allow(clippy::not_unsafe_ptr_arg_deref)]
343#[inline]
344pub fn open_key(
345 ctx: *mut RedisModuleCtx,
346 keyname: *mut RedisModuleString,
347 mode: KeyMode,
348) -> *mut RedisModuleKey {
349 unsafe { RedisModule_OpenKey.unwrap()(ctx, keyname, mode.bits()).cast::<RedisModuleKey>() }
350}
351
352#[allow(clippy::not_unsafe_ptr_arg_deref)]
353#[inline]
354pub(crate) fn open_key_with_flags(
355 ctx: *mut RedisModuleCtx,
356 keyname: *mut RedisModuleString,
357 mode: KeyMode,
358 flags: c_int,
359) -> *mut RedisModuleKey {
360 unsafe {
361 RedisModule_OpenKey.unwrap()(ctx, keyname, mode.bits() | flags).cast::<RedisModuleKey>()
362 }
363}
364
365#[allow(clippy::not_unsafe_ptr_arg_deref)]
366#[inline]
367pub fn reply_with_array(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
368 unsafe { RedisModule_ReplyWithArray.unwrap()(ctx, len).into() }
369}
370
371#[allow(clippy::not_unsafe_ptr_arg_deref)]
372#[inline]
373pub fn reply_with_map(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
374 unsafe {
375 RedisModule_ReplyWithMap
376 .map_or_else(
377 || RedisModule_ReplyWithArray.unwrap()(ctx, len * 2),
378 |f| f(ctx, len),
379 )
380 .into()
381 }
382}
383
384#[allow(clippy::not_unsafe_ptr_arg_deref)]
385#[inline]
386pub fn reply_with_set(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
387 unsafe {
388 RedisModule_ReplyWithSet
389 .map_or_else(
390 || RedisModule_ReplyWithArray.unwrap()(ctx, len * 2),
391 |f| f(ctx, len),
392 )
393 .into()
394 }
395}
396
397#[allow(clippy::not_unsafe_ptr_arg_deref)]
398#[inline]
399pub fn reply_with_attribute(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
400 unsafe { RedisModule_ReplyWithAttribute.unwrap()(ctx, len).into() }
401}
402
403#[allow(clippy::not_unsafe_ptr_arg_deref)]
404pub fn reply_with_error(ctx: *mut RedisModuleCtx, err: *const c_char) {
405 unsafe {
406 let msg = Context::str_as_legal_resp_string(CStr::from_ptr(err).to_str().unwrap());
407 RedisModule_ReplyWithError.unwrap()(ctx, msg.as_ptr());
408 }
409}
410
411#[allow(clippy::not_unsafe_ptr_arg_deref)]
412#[inline]
413pub fn reply_with_null(ctx: *mut RedisModuleCtx) -> Status {
414 unsafe { RedisModule_ReplyWithNull.unwrap()(ctx).into() }
415}
416
417#[allow(clippy::not_unsafe_ptr_arg_deref)]
418#[inline]
419pub fn reply_with_bool(ctx: *mut RedisModuleCtx, b: c_int) -> Status {
420 unsafe { RedisModule_ReplyWithBool.unwrap()(ctx, b).into() }
421}
422
423#[allow(clippy::not_unsafe_ptr_arg_deref)]
424#[inline]
425pub fn reply_with_long_long(ctx: *mut RedisModuleCtx, ll: c_longlong) -> Status {
426 unsafe { RedisModule_ReplyWithLongLong.unwrap()(ctx, ll).into() }
427}
428
429#[allow(clippy::not_unsafe_ptr_arg_deref)]
430#[inline]
431pub fn reply_with_double(ctx: *mut RedisModuleCtx, f: c_double) -> Status {
432 unsafe { RedisModule_ReplyWithDouble.unwrap()(ctx, f).into() }
433}
434
435#[allow(clippy::not_unsafe_ptr_arg_deref)]
436#[inline]
437pub fn reply_with_string(ctx: *mut RedisModuleCtx, s: *mut RedisModuleString) -> Status {
438 unsafe { RedisModule_ReplyWithString.unwrap()(ctx, s).into() }
439}
440
441#[allow(clippy::not_unsafe_ptr_arg_deref)]
442#[inline]
443pub fn reply_with_simple_string(ctx: *mut RedisModuleCtx, s: *const c_char) -> Status {
444 unsafe { RedisModule_ReplyWithSimpleString.unwrap()(ctx, s).into() }
445}
446
447#[allow(clippy::not_unsafe_ptr_arg_deref)]
448#[inline]
449pub fn reply_with_string_buffer(ctx: *mut RedisModuleCtx, s: *const c_char, len: size_t) -> Status {
450 unsafe { RedisModule_ReplyWithStringBuffer.unwrap()(ctx, s, len).into() }
451}
452
453#[allow(clippy::not_unsafe_ptr_arg_deref)]
454#[inline]
455pub fn reply_with_big_number(ctx: *mut RedisModuleCtx, s: *const c_char, len: size_t) -> Status {
456 unsafe { RedisModule_ReplyWithBigNumber.unwrap()(ctx, s, len).into() }
457}
458
459#[allow(clippy::not_unsafe_ptr_arg_deref)]
460#[inline]
461pub fn reply_with_verbatim_string(
462 ctx: *mut RedisModuleCtx,
463 s: *const c_char,
464 len: size_t,
465 format: *const c_char,
466) -> Status {
467 unsafe { RedisModule_ReplyWithVerbatimStringType.unwrap()(ctx, s, len, format).into() }
468}
469
470#[allow(clippy::not_unsafe_ptr_arg_deref)]
474#[inline]
475pub fn set_expire(key: *mut RedisModuleKey, expire: c_longlong) -> Status {
476 unsafe { RedisModule_SetExpire.unwrap()(key, expire).into() }
477}
478
479#[allow(clippy::not_unsafe_ptr_arg_deref)]
480#[inline]
481pub fn string_dma(key: *mut RedisModuleKey, len: *mut size_t, mode: KeyMode) -> *mut c_char {
482 unsafe { RedisModule_StringDMA.unwrap()(key, len, mode.bits()) }
483}
484
485#[allow(clippy::not_unsafe_ptr_arg_deref)]
486#[inline]
487pub fn string_truncate(key: *mut RedisModuleKey, new_len: size_t) -> Status {
488 unsafe { RedisModule_StringTruncate.unwrap()(key, new_len).into() }
489}
490
491#[allow(clippy::not_unsafe_ptr_arg_deref)]
492pub fn hash_get_multi<T>(
493 key: *mut RedisModuleKey,
494 fields: &[T],
495 values: &mut [*mut RedisModuleString],
496) -> Result<(), RedisError>
497where
498 T: Into<Vec<u8>> + Clone,
499{
500 assert_eq!(fields.len(), values.len());
501
502 let fields = fields
503 .iter()
504 .map(|e| CString::new(e.clone()))
505 .collect::<Result<Vec<CString>, _>>()?;
506
507 let mut fi = fields.iter();
508 let mut vi = values.iter_mut();
509
510 macro_rules! rm {
511 () => { unsafe {
512 RedisModule_HashGet.unwrap()(key, REDISMODULE_HASH_CFIELDS as i32,
513 ptr::null::<c_char>())
514 }};
515 ($($args:expr)*) => { unsafe {
516 RedisModule_HashGet.unwrap()(
517 key, REDISMODULE_HASH_CFIELDS as i32,
518 $($args),*,
519 ptr::null::<c_char>()
520 )
521 }};
522 }
523 macro_rules! f {
524 () => {
525 fi.next().unwrap().as_ptr()
526 };
527 }
528 macro_rules! v {
529 () => {
530 vi.next().unwrap()
531 };
532 }
533
534 let res = Status::from(match fields.len() {
539 0 => rm! {},
540 1 => rm! {f!() v!()},
541 2 => rm! {f!() v!() f!() v!()},
542 3 => rm! {f!() v!() f!() v!() f!() v!()},
543 4 => rm! {f!() v!() f!() v!() f!() v!() f!() v!()},
544 5 => rm! {f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()},
545 6 => rm! {f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()},
546 7 => rm! {
547 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
548 f!() v!()
549 },
550 8 => rm! {
551 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
552 f!() v!() f!() v!()
553 },
554 9 => rm! {
555 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
556 f!() v!() f!() v!() f!() v!()
557 },
558 10 => rm! {
559 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
560 f!() v!() f!() v!() f!() v!() f!() v!()
561 },
562 11 => rm! {
563 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
564 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
565 },
566 12 => rm! {
567 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
568 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
569 },
570 _ => panic!("Unsupported length"),
571 });
572
573 match res {
574 Status::Ok => Ok(()),
575 Status::Err => Err(RedisError::Str("ERR key is not a hash value")),
576 }
577}
578
579#[allow(clippy::not_unsafe_ptr_arg_deref)]
580#[inline]
581pub fn hash_set(key: *mut RedisModuleKey, field: &str, value: *mut RedisModuleString) -> Status {
582 let field = CString::new(field).unwrap();
583
584 unsafe {
585 RedisModule_HashSet.unwrap()(
586 key,
587 REDISMODULE_HASH_CFIELDS as i32,
588 field.as_ptr(),
589 value,
590 ptr::null::<c_char>(),
591 )
592 .into()
593 }
594}
595
596#[allow(clippy::not_unsafe_ptr_arg_deref)]
597#[inline]
598pub fn hash_del(key: *mut RedisModuleKey, field: &str) -> Status {
599 let field = CString::new(field).unwrap();
600
601 unsafe {
606 RedisModule_HashSet.unwrap()(
607 key,
608 REDISMODULE_HASH_CFIELDS as i32,
609 field.as_ptr(),
610 REDISMODULE_HASH_DELETE,
611 ptr::null::<c_char>(),
612 )
613 .into()
614 }
615}
616
617#[allow(clippy::not_unsafe_ptr_arg_deref)]
618#[inline]
619pub fn list_push(
620 key: *mut RedisModuleKey,
621 list_where: Where,
622 element: *mut RedisModuleString,
623) -> Status {
624 unsafe { RedisModule_ListPush.unwrap()(key, list_where as i32, element).into() }
625}
626
627#[allow(clippy::not_unsafe_ptr_arg_deref)]
628#[inline]
629pub fn list_pop(key: *mut RedisModuleKey, list_where: Where) -> *mut RedisModuleString {
630 unsafe { RedisModule_ListPop.unwrap()(key, list_where as i32) }
631}
632
633#[allow(clippy::not_unsafe_ptr_arg_deref)]
635#[inline]
636pub fn string_ptr_len(s: *const RedisModuleString, len: *mut size_t) -> *const c_char {
637 unsafe { RedisModule_StringPtrLen.unwrap()(s, len) }
638}
639
640#[allow(clippy::not_unsafe_ptr_arg_deref)]
641#[inline]
642pub fn string_retain_string(ctx: *mut RedisModuleCtx, s: *mut RedisModuleString) {
643 unsafe { RedisModule_RetainString.unwrap()(ctx, s) }
644}
645
646#[allow(clippy::not_unsafe_ptr_arg_deref)]
647#[inline]
648pub fn string_to_longlong(s: *const RedisModuleString, len: *mut i64) -> Status {
649 unsafe { RedisModule_StringToLongLong.unwrap()(s, len).into() }
650}
651
652#[allow(clippy::not_unsafe_ptr_arg_deref)]
653#[inline]
654pub fn string_to_double(s: *const RedisModuleString, len: *mut f64) -> Status {
655 unsafe { RedisModule_StringToDouble.unwrap()(s, len).into() }
656}
657
658#[allow(clippy::not_unsafe_ptr_arg_deref)]
659#[inline]
660pub fn string_set(key: *mut RedisModuleKey, s: *mut RedisModuleString) -> Status {
661 unsafe { RedisModule_StringSet.unwrap()(key, s).into() }
662}
663
664#[allow(clippy::not_unsafe_ptr_arg_deref)]
665#[inline]
666pub fn replicate_verbatim(ctx: *mut RedisModuleCtx) -> Status {
667 unsafe { RedisModule_ReplicateVerbatim.unwrap()(ctx).into() }
668}
669
670fn load<F, T>(rdb: *mut RedisModuleIO, f: F) -> Result<T, Error>
671where
672 F: FnOnce(*mut RedisModuleIO) -> T,
673{
674 let res = f(rdb);
675 if is_io_error(rdb) {
676 Err(RedisError::short_read().into())
677 } else {
678 Ok(res)
679 }
680}
681
682#[allow(clippy::not_unsafe_ptr_arg_deref)]
683pub fn load_unsigned(rdb: *mut RedisModuleIO) -> Result<u64, Error> {
684 unsafe { load(rdb, |rdb| RedisModule_LoadUnsigned.unwrap()(rdb)) }
685}
686
687#[allow(clippy::not_unsafe_ptr_arg_deref)]
688pub fn load_signed(rdb: *mut RedisModuleIO) -> Result<i64, Error> {
689 unsafe { load(rdb, |rdb| RedisModule_LoadSigned.unwrap()(rdb)) }
690}
691
692#[allow(clippy::not_unsafe_ptr_arg_deref)]
693pub fn load_string(rdb: *mut RedisModuleIO) -> Result<RedisString, Error> {
694 let p = unsafe { load(rdb, |rdb| RedisModule_LoadString.unwrap()(rdb))? };
695 Ok(RedisString::from_redis_module_string(ptr::null_mut(), p))
696}
697
698#[allow(clippy::not_unsafe_ptr_arg_deref)]
699pub fn load_string_buffer(rdb: *mut RedisModuleIO) -> Result<RedisBuffer, Error> {
700 unsafe {
701 let mut len = 0;
702 let buffer = load(rdb, |rdb| {
703 RedisModule_LoadStringBuffer.unwrap()(rdb, &mut len)
704 })?;
705 Ok(RedisBuffer::new(buffer, len))
706 }
707}
708
709#[allow(clippy::not_unsafe_ptr_arg_deref)]
710pub fn replicate<'a, T: Into<StrCallArgs<'a>>>(
711 ctx: *mut RedisModuleCtx,
712 command: &str,
713 args: T,
714) -> Status {
715 let mut call_args: StrCallArgs = args.into();
716 let final_args = call_args.args_mut();
717
718 let cmd = CString::new(command).unwrap();
719
720 unsafe {
721 RedisModule_Replicate.unwrap()(
722 ctx,
723 cmd.as_ptr(),
724 FMT,
725 final_args.as_ptr(),
726 final_args.len(),
727 )
728 .into()
729 }
730}
731
732#[allow(clippy::not_unsafe_ptr_arg_deref)]
733pub fn load_double(rdb: *mut RedisModuleIO) -> Result<f64, Error> {
734 unsafe { load(rdb, |rdb| RedisModule_LoadDouble.unwrap()(rdb)) }
735}
736
737#[allow(clippy::not_unsafe_ptr_arg_deref)]
738pub fn load_float(rdb: *mut RedisModuleIO) -> Result<f32, Error> {
739 unsafe { load(rdb, |rdb| RedisModule_LoadFloat.unwrap()(rdb)) }
740}
741
742#[allow(clippy::not_unsafe_ptr_arg_deref)]
743pub fn save_string(rdb: *mut RedisModuleIO, buf: &str) {
744 unsafe { RedisModule_SaveStringBuffer.unwrap()(rdb, buf.as_ptr().cast::<c_char>(), buf.len()) };
745}
746
747#[allow(clippy::not_unsafe_ptr_arg_deref)]
748pub fn save_redis_string(rdb: *mut RedisModuleIO, s: &RedisString) {
750 unsafe { RedisModule_SaveString.unwrap()(rdb, s.inner) };
751}
752
753#[allow(clippy::not_unsafe_ptr_arg_deref)]
754pub fn save_slice(rdb: *mut RedisModuleIO, buf: &[u8]) {
756 unsafe { RedisModule_SaveStringBuffer.unwrap()(rdb, buf.as_ptr().cast::<c_char>(), buf.len()) };
757}
758
759#[allow(clippy::not_unsafe_ptr_arg_deref)]
760pub fn save_double(rdb: *mut RedisModuleIO, val: f64) {
761 unsafe { RedisModule_SaveDouble.unwrap()(rdb, val) };
762}
763
764#[allow(clippy::not_unsafe_ptr_arg_deref)]
765pub fn save_signed(rdb: *mut RedisModuleIO, val: i64) {
766 unsafe { RedisModule_SaveSigned.unwrap()(rdb, val) };
767}
768
769#[allow(clippy::not_unsafe_ptr_arg_deref)]
770pub fn save_float(rdb: *mut RedisModuleIO, val: f32) {
771 unsafe { RedisModule_SaveFloat.unwrap()(rdb, val) };
772}
773
774#[allow(clippy::not_unsafe_ptr_arg_deref)]
775pub fn save_unsigned(rdb: *mut RedisModuleIO, val: u64) {
776 unsafe { RedisModule_SaveUnsigned.unwrap()(rdb, val) };
777}
778
779#[allow(clippy::not_unsafe_ptr_arg_deref)]
780pub fn string_compare(a: *mut RedisModuleString, b: *mut RedisModuleString) -> Ordering {
781 unsafe { RedisModule_StringCompare.unwrap()(a, b).cmp(&0) }
782}
783
784#[allow(clippy::not_unsafe_ptr_arg_deref)]
785pub fn string_append_buffer(
786 ctx: *mut RedisModuleCtx,
787 s: *mut RedisModuleString,
788 buff: &str,
789) -> Status {
790 unsafe {
791 RedisModule_StringAppendBuffer.unwrap()(ctx, s, buff.as_ptr().cast::<c_char>(), buff.len())
792 .into()
793 }
794}
795
796#[allow(clippy::not_unsafe_ptr_arg_deref)]
797pub fn subscribe_to_server_event(
798 ctx: *mut RedisModuleCtx,
799 event: RedisModuleEvent,
800 callback: RedisModuleEventCallback,
801) -> Status {
802 unsafe { RedisModule_SubscribeToServerEvent.unwrap()(ctx, event, callback).into() }
803}
804
805#[allow(clippy::not_unsafe_ptr_arg_deref)]
806pub fn register_info_function(ctx: *mut RedisModuleCtx, callback: RedisModuleInfoFunc) -> Status {
807 unsafe { RedisModule_RegisterInfoFunc.unwrap()(ctx, callback).into() }
808}
809
810#[allow(clippy::not_unsafe_ptr_arg_deref)]
811pub fn add_info_section(ctx: *mut RedisModuleInfoCtx, name: Option<&str>) -> Status {
812 name.map(|n| CString::new(n).unwrap()).map_or_else(
813 || unsafe { RedisModule_InfoAddSection.unwrap()(ctx, ptr::null_mut()).into() },
814 |n| unsafe { RedisModule_InfoAddSection.unwrap()(ctx, n.as_ptr()).into() },
815 )
816}
817
818#[allow(clippy::not_unsafe_ptr_arg_deref)]
819pub fn add_info_field_str(ctx: *mut RedisModuleInfoCtx, name: &str, content: &str) -> Status {
820 let name = CString::new(name).unwrap();
821 let content = RedisString::create(None, content);
822 unsafe { RedisModule_InfoAddFieldString.unwrap()(ctx, name.as_ptr(), content.inner).into() }
823}
824
825#[allow(clippy::not_unsafe_ptr_arg_deref)]
826pub fn add_info_field_long_long(
827 ctx: *mut RedisModuleInfoCtx,
828 name: &str,
829 value: c_longlong,
830) -> Status {
831 let name = CString::new(name).unwrap();
832 unsafe { RedisModule_InfoAddFieldLongLong.unwrap()(ctx, name.as_ptr(), value).into() }
833}
834
835#[allow(clippy::not_unsafe_ptr_arg_deref)]
836pub fn add_info_field_unsigned_long_long(
837 ctx: *mut RedisModuleInfoCtx,
838 name: &str,
839 value: c_ulonglong,
840) -> Status {
841 let name = CString::new(name).unwrap();
842 unsafe { RedisModule_InfoAddFieldULongLong.unwrap()(ctx, name.as_ptr(), value).into() }
843}
844
845#[allow(clippy::not_unsafe_ptr_arg_deref)]
846pub fn add_info_field_double(ctx: *mut RedisModuleInfoCtx, name: &str, value: c_double) -> Status {
847 let name = CString::new(name).unwrap();
848 unsafe { RedisModule_InfoAddFieldDouble.unwrap()(ctx, name.as_ptr(), value).into() }
849}
850
851#[allow(clippy::not_unsafe_ptr_arg_deref)]
852pub fn add_info_begin_dict_field(ctx: *mut RedisModuleInfoCtx, name: &str) -> Status {
853 let name = CString::new(name).unwrap();
854 unsafe { RedisModule_InfoBeginDictField.unwrap()(ctx, name.as_ptr()).into() }
855}
856
857#[allow(clippy::not_unsafe_ptr_arg_deref)]
858pub fn add_info_end_dict_field(ctx: *mut RedisModuleInfoCtx) -> Status {
859 unsafe { RedisModule_InfoEndDictField.unwrap()(ctx).into() }
860}
861
862pub unsafe fn export_shared_api(
871 ctx: *mut RedisModuleCtx,
872 func: *const ::std::os::raw::c_void,
873 name: *const ::std::os::raw::c_char,
874) {
875 RedisModule_ExportSharedAPI.unwrap()(ctx, name, func as *mut ::std::os::raw::c_void);
876}
877
878pub unsafe fn notify_keyspace_event(
887 ctx: *mut RedisModuleCtx,
888 event_type: NotifyEvent,
889 event: &str,
890 keyname: &RedisString,
891) -> Status {
892 let event = CString::new(event).unwrap();
893 RedisModule_NotifyKeyspaceEvent.unwrap()(ctx, event_type.bits(), event.as_ptr(), keyname.inner)
894 .into()
895}
896
897#[must_use]
901pub fn get_keyspace_events() -> NotifyEvent {
902 unsafe {
903 let events = RedisModule_GetNotifyKeyspaceEvents.unwrap()();
904 NotifyEvent::from_bits_truncate(events)
905 }
906}
907
908#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
909pub struct Version {
910 pub major: i32,
911 pub minor: i32,
912 pub patch: i32,
913}
914
915impl From<c_int> for Version {
916 fn from(ver: c_int) -> Self {
917 Self {
919 major: (ver & 0x00FF_0000) >> 16,
920 minor: (ver & 0x0000_FF00) >> 8,
921 patch: (ver & 0x0000_00FF),
922 }
923 }
924}
925
926#[allow(clippy::not_unsafe_ptr_arg_deref)]
927pub fn is_io_error(rdb: *mut RedisModuleIO) -> bool {
928 unsafe { RedisModule_IsIOError.unwrap()(rdb) != 0 }
929}