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
206#[allow(improper_ctypes)]
209#[link(name = "valkeymodule", kind = "static")]
210extern "C" {
211 pub fn Export_ValkeyModule_Init(
212 ctx: *mut ValkeyModuleCtx,
213 module_name: *const c_char,
214 module_version: c_int,
215 api_version: c_int,
216 ) -> c_int;
217
218 pub fn Export_ValkeyModule_InitAPI(ctx: *mut ValkeyModuleCtx) -> c_void;
219}
220
221pub fn use_redis_module_api() -> bool {
224 if cfg!(feature = "use-redismodule-api") {
225 true
226 } else {
227 false
228 }
229}
230
231pub const FMT: *const c_char = b"v\0".as_ptr().cast::<c_char>();
234
235pub const REDISMODULE_HASH_DELETE: *const RedisModuleString = 1 as *const RedisModuleString;
239
240#[allow(clippy::not_unsafe_ptr_arg_deref)]
243pub fn call_reply_type(reply: *mut RedisModuleCallReply) -> ReplyType {
244 unsafe {
245 RedisModule_CallReplyType.unwrap()(reply).into()
247 }
248}
249
250#[allow(clippy::not_unsafe_ptr_arg_deref)]
251pub fn free_call_reply(reply: *mut RedisModuleCallReply) {
252 unsafe { RedisModule_FreeCallReply.unwrap()(reply) }
253}
254
255#[allow(clippy::not_unsafe_ptr_arg_deref)]
256pub fn call_reply_integer(reply: *mut RedisModuleCallReply) -> c_longlong {
257 unsafe { RedisModule_CallReplyInteger.unwrap()(reply) }
258}
259
260#[allow(clippy::not_unsafe_ptr_arg_deref)]
264pub fn call_reply_bool(reply: *mut RedisModuleCallReply) -> bool {
265 (unsafe { RedisModule_CallReplyBool.unwrap()(reply) } != 0)
266}
267
268#[allow(clippy::not_unsafe_ptr_arg_deref)]
272pub fn call_reply_double(reply: *mut RedisModuleCallReply) -> f64 {
273 unsafe { RedisModule_CallReplyDouble.unwrap()(reply) }
274}
275
276#[allow(clippy::not_unsafe_ptr_arg_deref)]
280pub fn call_reply_big_number(reply: *mut RedisModuleCallReply) -> Option<String> {
281 unsafe {
282 let mut len: size_t = 0;
283 let reply_string: *mut u8 =
284 RedisModule_CallReplyBigNumber.unwrap()(reply, &mut len) as *mut u8;
285 if reply_string.is_null() {
286 return None;
287 }
288 String::from_utf8(slice::from_raw_parts(reply_string, len).to_vec()).ok()
289 }
290}
291
292#[allow(clippy::not_unsafe_ptr_arg_deref)]
296pub fn call_reply_verbatim_string(reply: *mut RedisModuleCallReply) -> Option<(String, Vec<u8>)> {
297 unsafe {
298 let mut len: size_t = 0;
299 let format: *const u8 = "".as_ptr();
300 let reply_string: *mut u8 =
301 RedisModule_CallReplyVerbatim.unwrap()(reply, &mut len, &mut (format as *const c_char))
302 as *mut u8;
303 if reply_string.is_null() {
304 return None;
305 }
306 Some((
307 String::from_utf8(slice::from_raw_parts(format, 3).to_vec()).ok()?,
308 slice::from_raw_parts(reply_string, len).to_vec(),
309 ))
310 }
311}
312
313#[allow(clippy::not_unsafe_ptr_arg_deref)]
314pub fn call_reply_array_element(
315 reply: *mut RedisModuleCallReply,
316 idx: usize,
317) -> *mut RedisModuleCallReply {
318 unsafe { RedisModule_CallReplyArrayElement.unwrap()(reply, idx) }
319}
320
321#[allow(clippy::not_unsafe_ptr_arg_deref)]
325pub fn call_reply_set_element(
326 reply: *mut RedisModuleCallReply,
327 idx: usize,
328) -> *mut RedisModuleCallReply {
329 unsafe { RedisModule_CallReplySetElement.unwrap()(reply, idx) }
330}
331
332#[allow(clippy::not_unsafe_ptr_arg_deref)]
336pub fn call_reply_map_element(
337 reply: *mut RedisModuleCallReply,
338 idx: usize,
339) -> (*mut RedisModuleCallReply, *mut RedisModuleCallReply) {
340 let mut key: *mut RedisModuleCallReply = ptr::null_mut();
341 let mut val: *mut RedisModuleCallReply = ptr::null_mut();
342 unsafe { RedisModule_CallReplyMapElement.unwrap()(reply, idx, &mut key, &mut val) };
343 (key, val)
344}
345
346#[allow(clippy::not_unsafe_ptr_arg_deref)]
347pub fn call_reply_length(reply: *mut RedisModuleCallReply) -> usize {
348 unsafe { RedisModule_CallReplyLength.unwrap()(reply) }
349}
350
351#[allow(clippy::not_unsafe_ptr_arg_deref)]
352pub fn call_reply_string_ptr(reply: *mut RedisModuleCallReply, len: *mut size_t) -> *const c_char {
353 unsafe { RedisModule_CallReplyStringPtr.unwrap()(reply, len) }
354}
355
356#[allow(clippy::not_unsafe_ptr_arg_deref)]
357pub fn call_reply_string(reply: *mut RedisModuleCallReply) -> Option<String> {
358 unsafe {
359 let mut len: size_t = 0;
360 let reply_string: *mut u8 =
361 RedisModule_CallReplyStringPtr.unwrap()(reply, &mut len) as *mut u8;
362 if reply_string.is_null() {
363 return None;
364 }
365 String::from_utf8(slice::from_raw_parts(reply_string, len).to_vec()).ok()
366 }
367}
368
369#[allow(clippy::not_unsafe_ptr_arg_deref)]
370#[inline]
371pub fn close_key(kp: *mut RedisModuleKey) {
372 unsafe { RedisModule_CloseKey.unwrap()(kp) }
373}
374
375#[allow(clippy::not_unsafe_ptr_arg_deref)]
376#[inline]
377pub fn open_key(
378 ctx: *mut RedisModuleCtx,
379 keyname: *mut RedisModuleString,
380 mode: KeyMode,
381) -> *mut RedisModuleKey {
382 unsafe { RedisModule_OpenKey.unwrap()(ctx, keyname, mode.bits()).cast::<RedisModuleKey>() }
383}
384
385#[allow(clippy::not_unsafe_ptr_arg_deref)]
386#[inline]
387pub(crate) fn open_key_with_flags(
388 ctx: *mut RedisModuleCtx,
389 keyname: *mut RedisModuleString,
390 mode: KeyMode,
391 flags: c_int,
392) -> *mut RedisModuleKey {
393 unsafe {
394 RedisModule_OpenKey.unwrap()(ctx, keyname, mode.bits() | flags).cast::<RedisModuleKey>()
395 }
396}
397
398#[allow(clippy::not_unsafe_ptr_arg_deref)]
399#[inline]
400pub fn reply_with_array(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
401 unsafe { RedisModule_ReplyWithArray.unwrap()(ctx, len).into() }
402}
403
404#[allow(clippy::not_unsafe_ptr_arg_deref)]
405#[inline]
406pub fn reply_with_map(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
407 unsafe {
408 RedisModule_ReplyWithMap
409 .map_or_else(
410 || RedisModule_ReplyWithArray.unwrap()(ctx, len * 2),
411 |f| f(ctx, len),
412 )
413 .into()
414 }
415}
416
417#[allow(clippy::not_unsafe_ptr_arg_deref)]
418#[inline]
419pub fn reply_with_set(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
420 unsafe {
421 RedisModule_ReplyWithSet
422 .map_or_else(
423 || RedisModule_ReplyWithArray.unwrap()(ctx, len * 2),
424 |f| f(ctx, len),
425 )
426 .into()
427 }
428}
429
430#[allow(clippy::not_unsafe_ptr_arg_deref)]
431#[inline]
432pub fn reply_with_attribute(ctx: *mut RedisModuleCtx, len: c_long) -> Status {
433 unsafe { RedisModule_ReplyWithAttribute.unwrap()(ctx, len).into() }
434}
435
436#[allow(clippy::not_unsafe_ptr_arg_deref)]
437pub fn reply_with_error(ctx: *mut RedisModuleCtx, err: *const c_char) {
438 unsafe {
439 let msg = Context::str_as_legal_resp_string(CStr::from_ptr(err).to_str().unwrap());
440 RedisModule_ReplyWithError.unwrap()(ctx, msg.as_ptr());
441 }
442}
443
444#[allow(clippy::not_unsafe_ptr_arg_deref)]
445#[inline]
446pub fn reply_with_null(ctx: *mut RedisModuleCtx) -> Status {
447 unsafe { RedisModule_ReplyWithNull.unwrap()(ctx).into() }
448}
449
450#[allow(clippy::not_unsafe_ptr_arg_deref)]
451#[inline]
452pub fn reply_with_bool(ctx: *mut RedisModuleCtx, b: c_int) -> Status {
453 unsafe { RedisModule_ReplyWithBool.unwrap()(ctx, b).into() }
454}
455
456#[allow(clippy::not_unsafe_ptr_arg_deref)]
457#[inline]
458pub fn reply_with_long_long(ctx: *mut RedisModuleCtx, ll: c_longlong) -> Status {
459 unsafe { RedisModule_ReplyWithLongLong.unwrap()(ctx, ll).into() }
460}
461
462#[allow(clippy::not_unsafe_ptr_arg_deref)]
463#[inline]
464pub fn reply_with_double(ctx: *mut RedisModuleCtx, f: c_double) -> Status {
465 unsafe { RedisModule_ReplyWithDouble.unwrap()(ctx, f).into() }
466}
467
468#[allow(clippy::not_unsafe_ptr_arg_deref)]
469#[inline]
470pub fn reply_with_string(ctx: *mut RedisModuleCtx, s: *mut RedisModuleString) -> Status {
471 unsafe { RedisModule_ReplyWithString.unwrap()(ctx, s).into() }
472}
473
474#[allow(clippy::not_unsafe_ptr_arg_deref)]
475#[inline]
476pub fn reply_with_simple_string(ctx: *mut RedisModuleCtx, s: *const c_char) -> Status {
477 unsafe { RedisModule_ReplyWithSimpleString.unwrap()(ctx, s).into() }
478}
479
480#[allow(clippy::not_unsafe_ptr_arg_deref)]
481#[inline]
482pub fn reply_with_string_buffer(ctx: *mut RedisModuleCtx, s: *const c_char, len: size_t) -> Status {
483 unsafe { RedisModule_ReplyWithStringBuffer.unwrap()(ctx, s, len).into() }
484}
485
486#[allow(clippy::not_unsafe_ptr_arg_deref)]
487#[inline]
488pub fn reply_with_big_number(ctx: *mut RedisModuleCtx, s: *const c_char, len: size_t) -> Status {
489 unsafe { RedisModule_ReplyWithBigNumber.unwrap()(ctx, s, len).into() }
490}
491
492#[allow(clippy::not_unsafe_ptr_arg_deref)]
493#[inline]
494pub fn reply_with_verbatim_string(
495 ctx: *mut RedisModuleCtx,
496 s: *const c_char,
497 len: size_t,
498 format: *const c_char,
499) -> Status {
500 unsafe { RedisModule_ReplyWithVerbatimStringType.unwrap()(ctx, s, len, format).into() }
501}
502
503#[allow(clippy::not_unsafe_ptr_arg_deref)]
507#[inline]
508pub fn set_expire(key: *mut RedisModuleKey, expire: c_longlong) -> Status {
509 unsafe { RedisModule_SetExpire.unwrap()(key, expire).into() }
510}
511
512#[allow(clippy::not_unsafe_ptr_arg_deref)]
513#[inline]
514pub fn string_dma(key: *mut RedisModuleKey, len: *mut size_t, mode: KeyMode) -> *mut c_char {
515 unsafe { RedisModule_StringDMA.unwrap()(key, len, mode.bits()) }
516}
517
518#[allow(clippy::not_unsafe_ptr_arg_deref)]
519#[inline]
520pub fn string_truncate(key: *mut RedisModuleKey, new_len: size_t) -> Status {
521 unsafe { RedisModule_StringTruncate.unwrap()(key, new_len).into() }
522}
523
524#[allow(clippy::not_unsafe_ptr_arg_deref)]
525pub fn hash_get_multi<T>(
526 key: *mut RedisModuleKey,
527 fields: &[T],
528 values: &mut [*mut RedisModuleString],
529) -> Result<(), ValkeyError>
530where
531 T: Into<Vec<u8>> + Clone,
532{
533 assert_eq!(fields.len(), values.len());
534
535 let fields = fields
536 .iter()
537 .map(|e| CString::new(e.clone()))
538 .collect::<Result<Vec<CString>, _>>()?;
539
540 let mut fi = fields.iter();
541 let mut vi = values.iter_mut();
542
543 macro_rules! rm {
544 () => { unsafe {
545 RedisModule_HashGet.unwrap()(key, REDISMODULE_HASH_CFIELDS as i32,
546 ptr::null::<c_char>())
547 }};
548 ($($args:expr)*) => { unsafe {
549 RedisModule_HashGet.unwrap()(
550 key, REDISMODULE_HASH_CFIELDS as i32,
551 $($args),*,
552 ptr::null::<c_char>()
553 )
554 }};
555 }
556 macro_rules! f {
557 () => {
558 fi.next().unwrap().as_ptr()
559 };
560 }
561 macro_rules! v {
562 () => {
563 vi.next().unwrap()
564 };
565 }
566
567 let res = Status::from(match fields.len() {
572 0 => rm! {},
573 1 => rm! {f!() v!()},
574 2 => rm! {f!() v!() f!() v!()},
575 3 => rm! {f!() v!() f!() v!() f!() v!()},
576 4 => rm! {f!() v!() f!() v!() f!() v!() f!() v!()},
577 5 => rm! {f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()},
578 6 => rm! {f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()},
579 7 => rm! {
580 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
581 f!() v!()
582 },
583 8 => rm! {
584 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
585 f!() v!() f!() v!()
586 },
587 9 => rm! {
588 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
589 f!() v!() f!() v!() f!() v!()
590 },
591 10 => rm! {
592 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
593 f!() v!() f!() v!() f!() v!() f!() v!()
594 },
595 11 => rm! {
596 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
597 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
598 },
599 12 => rm! {
600 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
601 f!() v!() f!() v!() f!() v!() f!() v!() f!() v!() f!() v!()
602 },
603 _ => panic!("Unsupported length"),
604 });
605
606 match res {
607 Status::Ok => Ok(()),
608 Status::Err => Err(ValkeyError::Str("ERR key is not a hash value")),
609 }
610}
611
612#[allow(clippy::not_unsafe_ptr_arg_deref)]
613#[inline]
614pub fn hash_set(key: *mut RedisModuleKey, field: &str, value: *mut RedisModuleString) -> Status {
615 let field = CString::new(field).unwrap();
616
617 unsafe {
618 RedisModule_HashSet.unwrap()(
619 key,
620 REDISMODULE_HASH_CFIELDS as i32,
621 field.as_ptr(),
622 value,
623 ptr::null::<c_char>(),
624 )
625 .into()
626 }
627}
628
629#[allow(clippy::not_unsafe_ptr_arg_deref)]
630#[inline]
631pub fn hash_del(key: *mut RedisModuleKey, field: &str) -> Status {
632 let field = CString::new(field).unwrap();
633
634 unsafe {
639 RedisModule_HashSet.unwrap()(
640 key,
641 REDISMODULE_HASH_CFIELDS as i32,
642 field.as_ptr(),
643 REDISMODULE_HASH_DELETE,
644 ptr::null::<c_char>(),
645 )
646 .into()
647 }
648}
649
650#[allow(clippy::not_unsafe_ptr_arg_deref)]
651#[inline]
652pub fn list_push(
653 key: *mut RedisModuleKey,
654 list_where: Where,
655 element: *mut RedisModuleString,
656) -> Status {
657 unsafe { RedisModule_ListPush.unwrap()(key, list_where as i32, element).into() }
658}
659
660#[allow(clippy::not_unsafe_ptr_arg_deref)]
661#[inline]
662pub fn list_pop(key: *mut RedisModuleKey, list_where: Where) -> *mut RedisModuleString {
663 unsafe { RedisModule_ListPop.unwrap()(key, list_where as i32) }
664}
665
666#[allow(clippy::not_unsafe_ptr_arg_deref)]
668#[inline]
669pub fn string_ptr_len(s: *const RedisModuleString, len: *mut size_t) -> *const c_char {
670 unsafe { RedisModule_StringPtrLen.unwrap()(s, len) }
671}
672
673#[allow(clippy::not_unsafe_ptr_arg_deref)]
674#[inline]
675pub fn string_retain_string(ctx: *mut RedisModuleCtx, s: *mut RedisModuleString) {
676 unsafe { RedisModule_RetainString.unwrap()(ctx, s) }
677}
678
679#[allow(clippy::not_unsafe_ptr_arg_deref)]
680#[inline]
681pub fn string_to_longlong(s: *const RedisModuleString, len: *mut i64) -> Status {
682 unsafe { RedisModule_StringToLongLong.unwrap()(s, len).into() }
683}
684
685#[allow(clippy::not_unsafe_ptr_arg_deref)]
686#[inline]
687pub fn string_to_ulonglong(s: *const RedisModuleString, len: *mut u64) -> Status {
688 unsafe { RedisModule_StringToULongLong.unwrap()(s, len).into() }
689}
690
691#[allow(clippy::not_unsafe_ptr_arg_deref)]
692#[inline]
693pub fn string_to_double(s: *const RedisModuleString, len: *mut f64) -> Status {
694 unsafe { RedisModule_StringToDouble.unwrap()(s, len).into() }
695}
696
697#[allow(clippy::not_unsafe_ptr_arg_deref)]
698#[inline]
699pub fn string_set(key: *mut RedisModuleKey, s: *mut RedisModuleString) -> Status {
700 unsafe { RedisModule_StringSet.unwrap()(key, s).into() }
701}
702
703#[allow(clippy::not_unsafe_ptr_arg_deref)]
704#[inline]
705pub fn replicate_verbatim(ctx: *mut RedisModuleCtx) -> Status {
706 unsafe { RedisModule_ReplicateVerbatim.unwrap()(ctx).into() }
707}
708
709fn load<F, T>(rdb: *mut RedisModuleIO, f: F) -> Result<T, Error>
710where
711 F: FnOnce(*mut RedisModuleIO) -> T,
712{
713 let res = f(rdb);
714 if is_io_error(rdb) {
715 Err(ValkeyError::short_read().into())
716 } else {
717 Ok(res)
718 }
719}
720
721#[allow(clippy::not_unsafe_ptr_arg_deref)]
722pub fn load_unsigned(rdb: *mut RedisModuleIO) -> Result<u64, Error> {
723 unsafe { load(rdb, |rdb| RedisModule_LoadUnsigned.unwrap()(rdb)) }
724}
725
726#[allow(clippy::not_unsafe_ptr_arg_deref)]
727pub fn load_signed(rdb: *mut RedisModuleIO) -> Result<i64, Error> {
728 unsafe { load(rdb, |rdb| RedisModule_LoadSigned.unwrap()(rdb)) }
729}
730
731#[allow(clippy::not_unsafe_ptr_arg_deref)]
732pub fn load_string(rdb: *mut RedisModuleIO) -> Result<ValkeyString, Error> {
733 let p = unsafe { load(rdb, |rdb| RedisModule_LoadString.unwrap()(rdb))? };
734 Ok(ValkeyString::from_redis_module_string(ptr::null_mut(), p))
735}
736
737#[allow(clippy::not_unsafe_ptr_arg_deref)]
738pub fn load_string_buffer(rdb: *mut RedisModuleIO) -> Result<RedisBuffer, Error> {
739 unsafe {
740 let mut len = 0;
741 let buffer = load(rdb, |rdb| {
742 RedisModule_LoadStringBuffer.unwrap()(rdb, &mut len)
743 })?;
744 Ok(RedisBuffer::new(buffer, len))
745 }
746}
747
748#[allow(clippy::not_unsafe_ptr_arg_deref)]
749pub fn replicate<'a, T: Into<StrCallArgs<'a>>>(
750 ctx: *mut RedisModuleCtx,
751 command: &str,
752 args: T,
753) -> Status {
754 let mut call_args: StrCallArgs = args.into();
755 let final_args = call_args.args_mut();
756
757 let cmd = CString::new(command).unwrap();
758
759 unsafe {
760 RedisModule_Replicate.unwrap()(
761 ctx,
762 cmd.as_ptr(),
763 FMT,
764 final_args.as_ptr(),
765 final_args.len(),
766 )
767 .into()
768 }
769}
770
771#[allow(clippy::not_unsafe_ptr_arg_deref)]
772pub fn load_double(rdb: *mut RedisModuleIO) -> Result<f64, Error> {
773 unsafe { load(rdb, |rdb| RedisModule_LoadDouble.unwrap()(rdb)) }
774}
775
776#[allow(clippy::not_unsafe_ptr_arg_deref)]
777pub fn load_float(rdb: *mut RedisModuleIO) -> Result<f32, Error> {
778 unsafe { load(rdb, |rdb| RedisModule_LoadFloat.unwrap()(rdb)) }
779}
780
781#[allow(clippy::not_unsafe_ptr_arg_deref)]
782pub fn save_string(rdb: *mut RedisModuleIO, buf: &str) {
783 unsafe { RedisModule_SaveStringBuffer.unwrap()(rdb, buf.as_ptr().cast::<c_char>(), buf.len()) };
784}
785
786#[allow(clippy::not_unsafe_ptr_arg_deref)]
787pub fn save_redis_string(rdb: *mut RedisModuleIO, s: &ValkeyString) {
789 unsafe { RedisModule_SaveString.unwrap()(rdb, s.inner) };
790}
791
792#[allow(clippy::not_unsafe_ptr_arg_deref)]
793pub fn save_slice(rdb: *mut RedisModuleIO, buf: &[u8]) {
795 unsafe { RedisModule_SaveStringBuffer.unwrap()(rdb, buf.as_ptr().cast::<c_char>(), buf.len()) };
796}
797
798#[allow(clippy::not_unsafe_ptr_arg_deref)]
799pub fn save_double(rdb: *mut RedisModuleIO, val: f64) {
800 unsafe { RedisModule_SaveDouble.unwrap()(rdb, val) };
801}
802
803#[allow(clippy::not_unsafe_ptr_arg_deref)]
804pub fn save_signed(rdb: *mut RedisModuleIO, val: i64) {
805 unsafe { RedisModule_SaveSigned.unwrap()(rdb, val) };
806}
807
808#[allow(clippy::not_unsafe_ptr_arg_deref)]
809pub fn save_float(rdb: *mut RedisModuleIO, val: f32) {
810 unsafe { RedisModule_SaveFloat.unwrap()(rdb, val) };
811}
812
813#[allow(clippy::not_unsafe_ptr_arg_deref)]
814pub fn save_unsigned(rdb: *mut RedisModuleIO, val: u64) {
815 unsafe { RedisModule_SaveUnsigned.unwrap()(rdb, val) };
816}
817
818#[allow(clippy::not_unsafe_ptr_arg_deref)]
819pub fn string_compare(a: *mut RedisModuleString, b: *mut RedisModuleString) -> Ordering {
820 unsafe { RedisModule_StringCompare.unwrap()(a, b).cmp(&0) }
821}
822
823#[allow(clippy::not_unsafe_ptr_arg_deref)]
824pub fn string_append_buffer(
825 ctx: *mut RedisModuleCtx,
826 s: *mut RedisModuleString,
827 buff: &str,
828) -> Status {
829 unsafe {
830 RedisModule_StringAppendBuffer.unwrap()(ctx, s, buff.as_ptr().cast::<c_char>(), buff.len())
831 .into()
832 }
833}
834
835#[allow(clippy::not_unsafe_ptr_arg_deref)]
836pub fn subscribe_to_server_event(
837 ctx: *mut RedisModuleCtx,
838 event: RedisModuleEvent,
839 callback: RedisModuleEventCallback,
840) -> Status {
841 unsafe { RedisModule_SubscribeToServerEvent.unwrap()(ctx, event, callback).into() }
842}
843
844#[allow(clippy::not_unsafe_ptr_arg_deref)]
845pub fn register_info_function(ctx: *mut RedisModuleCtx, callback: RedisModuleInfoFunc) -> Status {
846 unsafe { RedisModule_RegisterInfoFunc.unwrap()(ctx, callback).into() }
847}
848
849#[allow(clippy::not_unsafe_ptr_arg_deref)]
850pub fn add_info_section(ctx: *mut RedisModuleInfoCtx, name: Option<&str>) -> Status {
851 name.map(|n| CString::new(n).unwrap()).map_or_else(
852 || unsafe { RedisModule_InfoAddSection.unwrap()(ctx, ptr::null_mut()).into() },
853 |n| unsafe { RedisModule_InfoAddSection.unwrap()(ctx, n.as_ptr()).into() },
854 )
855}
856
857#[allow(clippy::not_unsafe_ptr_arg_deref)]
858pub fn add_info_field_str(ctx: *mut RedisModuleInfoCtx, name: &str, content: &str) -> Status {
859 let name = CString::new(name).unwrap();
860 let content = ValkeyString::create(None, content);
861 unsafe { RedisModule_InfoAddFieldString.unwrap()(ctx, name.as_ptr(), content.inner).into() }
862}
863
864#[allow(clippy::not_unsafe_ptr_arg_deref)]
865pub fn add_info_field_long_long(
866 ctx: *mut RedisModuleInfoCtx,
867 name: &str,
868 value: c_longlong,
869) -> Status {
870 let name = CString::new(name).unwrap();
871 unsafe { RedisModule_InfoAddFieldLongLong.unwrap()(ctx, name.as_ptr(), value).into() }
872}
873
874#[allow(clippy::not_unsafe_ptr_arg_deref)]
875pub fn add_info_field_unsigned_long_long(
876 ctx: *mut RedisModuleInfoCtx,
877 name: &str,
878 value: c_ulonglong,
879) -> Status {
880 let name = CString::new(name).unwrap();
881 unsafe { RedisModule_InfoAddFieldULongLong.unwrap()(ctx, name.as_ptr(), value).into() }
882}
883
884#[allow(clippy::not_unsafe_ptr_arg_deref)]
885pub fn add_info_field_double(ctx: *mut RedisModuleInfoCtx, name: &str, value: c_double) -> Status {
886 let name = CString::new(name).unwrap();
887 unsafe { RedisModule_InfoAddFieldDouble.unwrap()(ctx, name.as_ptr(), value).into() }
888}
889
890#[allow(clippy::not_unsafe_ptr_arg_deref)]
891pub fn add_info_begin_dict_field(ctx: *mut RedisModuleInfoCtx, name: &str) -> Status {
892 let name = CString::new(name).unwrap();
893 unsafe { RedisModule_InfoBeginDictField.unwrap()(ctx, name.as_ptr()).into() }
894}
895
896#[allow(clippy::not_unsafe_ptr_arg_deref)]
897pub fn add_info_end_dict_field(ctx: *mut RedisModuleInfoCtx) -> Status {
898 unsafe { RedisModule_InfoEndDictField.unwrap()(ctx).into() }
899}
900
901pub unsafe fn export_shared_api(
910 ctx: *mut RedisModuleCtx,
911 func: *const ::std::os::raw::c_void,
912 name: *const ::std::os::raw::c_char,
913) {
914 RedisModule_ExportSharedAPI.unwrap()(ctx, name, func as *mut ::std::os::raw::c_void);
915}
916
917pub unsafe fn notify_keyspace_event(
926 ctx: *mut RedisModuleCtx,
927 event_type: NotifyEvent,
928 event: &str,
929 keyname: &ValkeyString,
930) -> Status {
931 let event = CString::new(event).unwrap();
932 RedisModule_NotifyKeyspaceEvent.unwrap()(ctx, event_type.bits(), event.as_ptr(), keyname.inner)
933 .into()
934}
935
936#[must_use]
940pub fn get_keyspace_events() -> NotifyEvent {
941 unsafe {
942 let events = RedisModule_GetNotifyKeyspaceEvents.unwrap()();
943 NotifyEvent::from_bits_truncate(events)
944 }
945}
946
947pub fn get_keyspace_notification_flags_all() -> NotifyEvent {
960 unsafe {
961 NotifyEvent::from_bits_truncate(RedisModule_GetKeyspaceNotificationFlagsAll.unwrap()())
962 }
963}
964
965#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
966pub struct Version {
967 pub major: i32,
968 pub minor: i32,
969 pub patch: i32,
970}
971
972impl From<c_int> for Version {
973 fn from(ver: c_int) -> Self {
974 Self {
976 major: (ver & 0x00FF_0000) >> 16,
977 minor: (ver & 0x0000_FF00) >> 8,
978 patch: (ver & 0x0000_00FF),
979 }
980 }
981}
982
983#[allow(clippy::not_unsafe_ptr_arg_deref)]
984pub fn is_io_error(rdb: *mut RedisModuleIO) -> bool {
985 unsafe { RedisModule_IsIOError.unwrap()(rdb) != 0 }
986}