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