Skip to main content

redis_module/context/
mod.rs

1use bitflags::bitflags;
2use redis_module_macros_internals::api;
3use std::collections::{BTreeMap, HashMap};
4use std::ffi::CString;
5use std::os::raw::c_void;
6use std::os::raw::{c_char, c_int, c_long, c_longlong};
7use std::ptr::{self, NonNull};
8use std::sync::atomic::{AtomicPtr, Ordering};
9
10use crate::key::{KeyFlags, RedisKey, RedisKeyWritable};
11use crate::logging::RedisLogLevel;
12use crate::raw::{ModuleOptions, Version};
13use crate::redisvalue::RedisValueKey;
14use crate::{
15    add_info_begin_dict_field, add_info_end_dict_field, add_info_field_double,
16    add_info_field_long_long, add_info_field_str, add_info_field_unsigned_long_long, raw, utils,
17    Status,
18};
19use crate::{add_info_section, RedisResult};
20use crate::{RedisError, RedisString, RedisValue};
21use std::ops::Deref;
22
23use std::ffi::CStr;
24
25use self::call_reply::{create_promise_call_reply, CallResult, PromiseCallReply};
26use self::thread_safe::RedisLockIndicator;
27
28mod timer;
29
30pub mod blocked;
31pub mod call_reply;
32pub mod commands;
33pub mod defrag;
34pub mod info;
35pub mod key_cursor;
36pub mod keys_cursor;
37pub mod server_events;
38pub mod thread_safe;
39
40pub struct CallOptionsBuilder {
41    options: String,
42}
43
44impl Default for CallOptionsBuilder {
45    fn default() -> Self {
46        CallOptionsBuilder {
47            options: "v".to_string(),
48        }
49    }
50}
51
52#[derive(Clone)]
53pub struct CallOptions {
54    options: CString,
55}
56
57#[derive(Clone)]
58#[cfg(any(
59    feature = "min-redis-compatibility-version-8-0",
60    feature = "min-redis-compatibility-version-7-4",
61    feature = "min-redis-compatibility-version-7-2"
62))]
63pub struct BlockingCallOptions {
64    options: CString,
65}
66
67#[derive(Copy, Clone)]
68pub enum CallOptionResp {
69    Resp2,
70    Resp3,
71    Auto,
72}
73
74impl CallOptionsBuilder {
75    pub fn new() -> CallOptionsBuilder {
76        Self::default()
77    }
78
79    fn add_flag(&mut self, flag: &str) {
80        self.options.push_str(flag);
81    }
82
83    /// Enable this option will not allow RM_Call to perform write commands
84    pub fn no_writes(mut self) -> CallOptionsBuilder {
85        self.add_flag("W");
86        self
87    }
88
89    /// Enable this option will run RM_Call is script mode.
90    /// This mean that Redis will enable the following protections:
91    /// 1. Not allow running dangerous commands like 'shutdown'
92    /// 2. Not allow running write commands on OOM or if there are not enough good replica's connected
93    pub fn script_mode(mut self) -> CallOptionsBuilder {
94        self.add_flag("S");
95        self
96    }
97
98    /// Enable this option will perform ACL validation on the user attached to the context that
99    /// is used to invoke the call.
100    pub fn verify_acl(mut self) -> CallOptionsBuilder {
101        self.add_flag("C");
102        self
103    }
104
105    /// Enable this option will OOM validation before running the command
106    pub fn verify_oom(mut self) -> CallOptionsBuilder {
107        self.add_flag("M");
108        self
109    }
110
111    /// Enable this option will return error as CallReply object instead of setting errno (it is
112    /// usually recommend to enable it)
113    pub fn errors_as_replies(mut self) -> CallOptionsBuilder {
114        self.add_flag("E");
115        self
116    }
117
118    /// Enable this option will cause the command to be replicaed to the replica and AOF
119    pub fn replicate(mut self) -> CallOptionsBuilder {
120        self.add_flag("!");
121        self
122    }
123
124    /// Allow control the protocol version in which the replies will be returned.
125    pub fn resp(mut self, resp: CallOptionResp) -> CallOptionsBuilder {
126        match resp {
127            CallOptionResp::Auto => self.add_flag("0"),
128            CallOptionResp::Resp2 => (),
129            CallOptionResp::Resp3 => self.add_flag("3"),
130        }
131        self
132    }
133
134    /// Construct a CallOption object that can be used to run commands using call_ext
135    pub fn build(self) -> CallOptions {
136        CallOptions {
137            options: CString::new(self.options).unwrap(), // the data will never contains internal \0 so it is safe to unwrap.
138        }
139    }
140
141    /// Construct a CallOption object that can be used to run commands using call_blocking.
142    /// The commands can be either blocking or none blocking. In case the command are blocking
143    /// (like `blpop`) a [FutureCallReply] will be returned.
144    #[cfg(any(
145        feature = "min-redis-compatibility-version-7-4",
146        feature = "min-redis-compatibility-version-7-2"
147    ))]
148    pub fn build_blocking(mut self) -> BlockingCallOptions {
149        self.add_flag("K");
150        BlockingCallOptions {
151            options: CString::new(self.options).unwrap(), // the data will never contains internal \0 so it is safe to unwrap.
152        }
153    }
154}
155
156/// This struct allows logging when the Redis GIL is not acquired.
157/// It is implemented `Send` and `Sync` so it can safely be used
158/// from within different threads.
159pub struct DetachedContext {
160    pub(crate) ctx: AtomicPtr<raw::RedisModuleCtx>,
161}
162
163impl DetachedContext {
164    pub const fn new() -> Self {
165        DetachedContext {
166            ctx: AtomicPtr::new(ptr::null_mut()),
167        }
168    }
169}
170
171impl Default for DetachedContext {
172    fn default() -> Self {
173        Self::new()
174    }
175}
176
177/// This object is returned after locking Redis from [DetachedContext].
178/// On dispose, Redis will be unlocked.
179/// This object implements [Deref] for [Context] so it can be used
180/// just like any Redis [Context] for command invocation.
181/// **This object should not be used to return replies** because there is
182/// no real client behind this context to return replies to.
183pub struct DetachedContextGuard {
184    pub(crate) ctx: Context,
185}
186
187unsafe impl RedisLockIndicator for DetachedContextGuard {}
188
189impl Drop for DetachedContextGuard {
190    fn drop(&mut self) {
191        unsafe {
192            raw::RedisModule_ThreadSafeContextUnlock.unwrap()(self.ctx.ctx);
193        };
194    }
195}
196
197impl Deref for DetachedContextGuard {
198    type Target = Context;
199
200    fn deref(&self) -> &Self::Target {
201        &self.ctx
202    }
203}
204
205impl DetachedContext {
206    pub fn log(&self, level: RedisLogLevel, message: &str) {
207        let c = self.ctx.load(Ordering::Relaxed);
208        crate::logging::log_internal(c, level, message);
209    }
210
211    pub fn log_debug(&self, message: &str) {
212        self.log(RedisLogLevel::Debug, message);
213    }
214
215    pub fn log_notice(&self, message: &str) {
216        self.log(RedisLogLevel::Notice, message);
217    }
218
219    pub fn log_verbose(&self, message: &str) {
220        self.log(RedisLogLevel::Verbose, message);
221    }
222
223    pub fn log_warning(&self, message: &str) {
224        self.log(RedisLogLevel::Warning, message);
225    }
226
227    pub fn set_context(&self, ctx: &Context) -> Result<(), RedisError> {
228        let c = self.ctx.load(Ordering::Relaxed);
229        if !c.is_null() {
230            return Err(RedisError::Str("Detached context is already set"));
231        }
232        let ctx = unsafe { raw::RedisModule_GetDetachedThreadSafeContext.unwrap()(ctx.ctx) };
233        self.ctx.store(ctx, Ordering::Relaxed);
234        Ok(())
235    }
236
237    /// Lock Redis for command invocation. Returns [DetachedContextGuard] which will unlock Redis when dispose.
238    /// [DetachedContextGuard] implements [Deref<Target = Context>] so it can be used just like any Redis [Context] for command invocation.
239    /// Locking Redis when Redis is already locked by the current thread is left unspecified.
240    /// However, this function will not return on the second call (it might panic or deadlock, for example)..
241    pub fn lock(&self) -> DetachedContextGuard {
242        let c = self.ctx.load(Ordering::Relaxed);
243        unsafe { raw::RedisModule_ThreadSafeContextLock.unwrap()(c) };
244        let ctx = Context::new(c);
245        DetachedContextGuard { ctx }
246    }
247}
248
249unsafe impl Send for DetachedContext {}
250unsafe impl Sync for DetachedContext {}
251
252/// `Context` is a structure that's designed to give us a high-level interface to
253/// the Redis module API by abstracting away the raw C FFI calls.
254#[derive(Debug)]
255pub struct Context {
256    pub ctx: *mut raw::RedisModuleCtx,
257}
258
259/// A guerd that protected a user that has
260/// been set on a context using `autenticate_user`.
261/// This guerd make sure to unset the user when freed.
262/// It prevent privilege escalation security issues
263/// that can happened by forgeting to unset the user.
264#[derive(Debug)]
265pub struct ContextUserScope<'ctx> {
266    ctx: &'ctx Context,
267    user: *mut raw::RedisModuleUser,
268}
269
270impl<'ctx> Drop for ContextUserScope<'ctx> {
271    fn drop(&mut self) {
272        self.ctx.deautenticate_user();
273        unsafe { raw::RedisModule_FreeModuleUser.unwrap()(self.user) };
274    }
275}
276
277impl<'ctx> ContextUserScope<'ctx> {
278    fn new(ctx: &'ctx Context, user: *mut raw::RedisModuleUser) -> ContextUserScope<'ctx> {
279        ContextUserScope { ctx, user }
280    }
281}
282
283pub struct StrCallArgs<'a> {
284    is_owner: bool,
285    args: Vec<*mut raw::RedisModuleString>,
286    // Phantom is used to make sure the object will not live longer than actual arguments slice
287    phantom: std::marker::PhantomData<&'a raw::RedisModuleString>,
288}
289
290impl<'a> Drop for StrCallArgs<'a> {
291    fn drop(&mut self) {
292        if self.is_owner {
293            self.args.iter_mut().for_each(|v| unsafe {
294                raw::RedisModule_FreeString.unwrap()(std::ptr::null_mut(), *v)
295            });
296        }
297    }
298}
299
300impl<'a, T: AsRef<[u8]> + ?Sized> From<&'a [&T]> for StrCallArgs<'a> {
301    fn from(vals: &'a [&T]) -> Self {
302        StrCallArgs {
303            is_owner: true,
304            args: vals
305                .iter()
306                .map(|v| RedisString::create_from_slice(std::ptr::null_mut(), v.as_ref()).take())
307                .collect(),
308            phantom: std::marker::PhantomData,
309        }
310    }
311}
312
313impl<'a> From<&'a [&RedisString]> for StrCallArgs<'a> {
314    fn from(vals: &'a [&RedisString]) -> Self {
315        StrCallArgs {
316            is_owner: false,
317            args: vals.iter().map(|v| v.inner).collect(),
318            phantom: std::marker::PhantomData,
319        }
320    }
321}
322
323impl<'a, const SIZE: usize, T: ?Sized> From<&'a [&T; SIZE]> for StrCallArgs<'a>
324where
325    for<'b> &'a [&'b T]: Into<StrCallArgs<'a>>,
326{
327    fn from(vals: &'a [&T; SIZE]) -> Self {
328        vals.as_ref().into()
329    }
330}
331
332impl<'a> StrCallArgs<'a> {
333    pub(crate) fn args_mut(&mut self) -> &mut [*mut raw::RedisModuleString] {
334        &mut self.args
335    }
336}
337
338impl Context {
339    pub const fn new(ctx: *mut raw::RedisModuleCtx) -> Self {
340        Self { ctx }
341    }
342
343    #[must_use]
344    pub const fn dummy() -> Self {
345        Self {
346            ctx: ptr::null_mut(),
347        }
348    }
349
350    pub fn log(&self, level: RedisLogLevel, message: &str) {
351        crate::logging::log_internal(self.ctx, level, message);
352    }
353
354    pub fn log_debug(&self, message: &str) {
355        self.log(RedisLogLevel::Debug, message);
356    }
357
358    pub fn log_notice(&self, message: &str) {
359        self.log(RedisLogLevel::Notice, message);
360    }
361
362    pub fn log_verbose(&self, message: &str) {
363        self.log(RedisLogLevel::Verbose, message);
364    }
365
366    pub fn log_warning(&self, message: &str) {
367        self.log(RedisLogLevel::Warning, message);
368    }
369
370    /// # Panics
371    ///
372    /// Will panic if `RedisModule_AutoMemory` is missing in redismodule.h
373    pub fn auto_memory(&self) {
374        unsafe {
375            raw::RedisModule_AutoMemory.unwrap()(self.ctx);
376        }
377    }
378
379    /// # Panics
380    ///
381    /// Will panic if `RedisModule_IsKeysPositionRequest` is missing in redismodule.h
382    #[must_use]
383    pub fn is_keys_position_request(&self) -> bool {
384        // We want this to be available in tests where we don't have an actual Redis to call
385        if cfg!(test) {
386            return false;
387        }
388
389        (unsafe { raw::RedisModule_IsKeysPositionRequest.unwrap()(self.ctx) }) != 0
390    }
391
392    /// # Panics
393    ///
394    /// Will panic if `RedisModule_KeyAtPos` is missing in redismodule.h
395    pub fn key_at_pos(&self, pos: i32) {
396        // TODO: This will crash redis if `pos` is out of range.
397        // Think of a way to make this safe by checking the range.
398        unsafe {
399            raw::RedisModule_KeyAtPos.unwrap()(self.ctx, pos as c_int);
400        }
401    }
402
403    fn call_internal<
404        'ctx,
405        'a,
406        T: Into<StrCallArgs<'a>>,
407        R: From<PromiseCallReply<'static, 'ctx>>,
408    >(
409        &'ctx self,
410        command: &str,
411        fmt: *const c_char,
412        args: T,
413    ) -> R {
414        let mut call_args: StrCallArgs = args.into();
415        let final_args = call_args.args_mut();
416
417        let cmd = CString::new(command).unwrap();
418        let reply: *mut raw::RedisModuleCallReply = unsafe {
419            let p_call = raw::RedisModule_Call.unwrap();
420            p_call(
421                self.ctx,
422                cmd.as_ptr(),
423                fmt,
424                final_args.as_mut_ptr(),
425                final_args.len(),
426            )
427        };
428        let promise = create_promise_call_reply(self, NonNull::new(reply));
429        R::from(promise)
430    }
431
432    pub fn call<'a, T: Into<StrCallArgs<'a>>>(&self, command: &str, args: T) -> RedisResult {
433        self.call_internal::<_, CallResult>(command, raw::FMT, args)
434            .map_or_else(|e| Err(e.into()), |v| Ok((&v).into()))
435    }
436
437    /// Invoke a command on Redis and return the result
438    /// Unlike 'call' this API also allow to pass a CallOption to control different aspects
439    /// of the command invocation.
440    pub fn call_ext<'a, T: Into<StrCallArgs<'a>>, R: From<CallResult<'static>>>(
441        &self,
442        command: &str,
443        options: &CallOptions,
444        args: T,
445    ) -> R {
446        let res: CallResult<'static> =
447            self.call_internal(command, options.options.as_ptr() as *const c_char, args);
448        R::from(res)
449    }
450
451    /// Same as [call_ext] but also allow to perform blocking commands like BLPOP.
452    #[cfg(any(
453        feature = "min-redis-compatibility-version-7-4",
454        feature = "min-redis-compatibility-version-7-2"
455    ))]
456    pub fn call_blocking<
457        'ctx,
458        'a,
459        T: Into<StrCallArgs<'a>>,
460        R: From<PromiseCallReply<'static, 'ctx>>,
461    >(
462        &'ctx self,
463        command: &str,
464        options: &BlockingCallOptions,
465        args: T,
466    ) -> R {
467        self.call_internal(command, options.options.as_ptr() as *const c_char, args)
468    }
469
470    #[must_use]
471    pub fn str_as_legal_resp_string(s: &str) -> CString {
472        CString::new(
473            s.chars()
474                .map(|c| match c {
475                    '\r' | '\n' | '\0' => b' ',
476                    _ => c as u8,
477                })
478                .collect::<Vec<_>>(),
479        )
480        .unwrap()
481    }
482
483    #[allow(clippy::must_use_candidate)]
484    pub fn reply_simple_string(&self, s: &str) -> raw::Status {
485        let msg = Self::str_as_legal_resp_string(s);
486        raw::reply_with_simple_string(self.ctx, msg.as_ptr())
487    }
488
489    #[allow(clippy::must_use_candidate)]
490    pub fn reply_error_string(&self, s: &str) -> raw::Status {
491        let msg = Self::str_as_legal_resp_string(s);
492        unsafe { raw::RedisModule_ReplyWithError.unwrap()(self.ctx, msg.as_ptr()).into() }
493    }
494
495    pub fn reply_with_key(&self, result: RedisValueKey) -> raw::Status {
496        match result {
497            RedisValueKey::Integer(i) => raw::reply_with_long_long(self.ctx, i),
498            RedisValueKey::String(s) => {
499                raw::reply_with_string_buffer(self.ctx, s.as_ptr().cast::<c_char>(), s.len())
500            }
501            RedisValueKey::BulkString(b) => {
502                raw::reply_with_string_buffer(self.ctx, b.as_ptr().cast::<c_char>(), b.len())
503            }
504            RedisValueKey::BulkRedisString(s) => raw::reply_with_string(self.ctx, s.inner),
505            RedisValueKey::Bool(b) => raw::reply_with_bool(self.ctx, b.into()),
506        }
507    }
508
509    /// # Panics
510    ///
511    /// Will panic if methods used are missing in redismodule.h
512    #[allow(clippy::must_use_candidate)]
513    pub fn reply(&self, result: RedisResult) -> raw::Status {
514        match result {
515            Ok(RedisValue::Bool(v)) => raw::reply_with_bool(self.ctx, v.into()),
516            Ok(RedisValue::Integer(v)) => raw::reply_with_long_long(self.ctx, v),
517            Ok(RedisValue::Float(v)) => raw::reply_with_double(self.ctx, v),
518            Ok(RedisValue::SimpleStringStatic(s)) => {
519                let msg = CString::new(s).unwrap();
520                raw::reply_with_simple_string(self.ctx, msg.as_ptr())
521            }
522
523            Ok(RedisValue::SimpleString(s)) => {
524                let msg = CString::new(s).unwrap();
525                raw::reply_with_simple_string(self.ctx, msg.as_ptr())
526            }
527
528            Ok(RedisValue::BulkString(s)) => {
529                raw::reply_with_string_buffer(self.ctx, s.as_ptr().cast::<c_char>(), s.len())
530            }
531
532            Ok(RedisValue::BigNumber(s)) => {
533                raw::reply_with_big_number(self.ctx, s.as_ptr().cast::<c_char>(), s.len())
534            }
535
536            Ok(RedisValue::VerbatimString((format, data))) => raw::reply_with_verbatim_string(
537                self.ctx,
538                data.as_ptr().cast(),
539                data.len(),
540                format.0.as_ptr().cast(),
541            ),
542
543            Ok(RedisValue::BulkRedisString(s)) => raw::reply_with_string(self.ctx, s.inner),
544
545            Ok(RedisValue::StringBuffer(s)) => {
546                raw::reply_with_string_buffer(self.ctx, s.as_ptr().cast::<c_char>(), s.len())
547            }
548
549            Ok(RedisValue::Array(array)) => {
550                raw::reply_with_array(self.ctx, array.len() as c_long);
551
552                for elem in array {
553                    self.reply(Ok(elem));
554                }
555
556                raw::Status::Ok
557            }
558
559            Ok(RedisValue::Map(map)) => {
560                raw::reply_with_map(self.ctx, map.len() as c_long);
561
562                for (key, value) in map {
563                    self.reply_with_key(key);
564                    self.reply(Ok(value));
565                }
566
567                raw::Status::Ok
568            }
569
570            Ok(RedisValue::OrderedMap(map)) => {
571                raw::reply_with_map(self.ctx, map.len() as c_long);
572
573                for (key, value) in map {
574                    self.reply_with_key(key);
575                    self.reply(Ok(value));
576                }
577
578                raw::Status::Ok
579            }
580
581            Ok(RedisValue::Set(set)) => {
582                raw::reply_with_set(self.ctx, set.len() as c_long);
583                set.into_iter().for_each(|e| {
584                    self.reply_with_key(e);
585                });
586
587                raw::Status::Ok
588            }
589
590            Ok(RedisValue::OrderedSet(set)) => {
591                raw::reply_with_set(self.ctx, set.len() as c_long);
592                set.into_iter().for_each(|e| {
593                    self.reply_with_key(e);
594                });
595
596                raw::Status::Ok
597            }
598
599            Ok(RedisValue::Null) => raw::reply_with_null(self.ctx),
600
601            Ok(RedisValue::NoReply) => raw::Status::Ok,
602
603            Ok(RedisValue::StaticError(s)) => self.reply_error_string(s),
604
605            Err(RedisError::WrongArity) => unsafe {
606                if self.is_keys_position_request() {
607                    // We can't return a result since we don't have a client
608                    raw::Status::Err
609                } else {
610                    raw::RedisModule_WrongArity.unwrap()(self.ctx).into()
611                }
612            },
613
614            Err(RedisError::WrongType) => {
615                self.reply_error_string(RedisError::WrongType.to_string().as_str())
616            }
617
618            Err(RedisError::String(s)) => self.reply_error_string(s.as_str()),
619
620            Err(RedisError::Str(s)) => self.reply_error_string(s),
621        }
622    }
623
624    #[must_use]
625    pub fn open_key(&self, key: &RedisString) -> RedisKey {
626        RedisKey::open(self.ctx, key)
627    }
628
629    #[must_use]
630    pub fn open_key_with_flags(&self, key: &RedisString, flags: KeyFlags) -> RedisKey {
631        RedisKey::open_with_flags(self.ctx, key, flags)
632    }
633
634    #[must_use]
635    pub fn open_key_writable(&self, key: &RedisString) -> RedisKeyWritable {
636        RedisKeyWritable::open(self.ctx, key)
637    }
638
639    #[must_use]
640    pub fn open_key_writable_with_flags(
641        &self,
642        key: &RedisString,
643        flags: KeyFlags,
644    ) -> RedisKeyWritable {
645        RedisKeyWritable::open_with_flags(self.ctx, key, flags)
646    }
647
648    pub fn replicate_verbatim(&self) {
649        raw::replicate_verbatim(self.ctx);
650    }
651
652    /// Replicate command to the replica and AOF.
653    pub fn replicate<'a, T: Into<StrCallArgs<'a>>>(&self, command: &str, args: T) {
654        raw::replicate(self.ctx, command, args);
655    }
656
657    #[must_use]
658    pub fn create_string<T: Into<Vec<u8>>>(&self, s: T) -> RedisString {
659        RedisString::create(NonNull::new(self.ctx), s)
660    }
661
662    #[must_use]
663    pub const fn get_raw(&self) -> *mut raw::RedisModuleCtx {
664        self.ctx
665    }
666
667    /// # Safety
668    ///
669    /// See [raw::export_shared_api].
670    pub unsafe fn export_shared_api(
671        &self,
672        func: *const ::std::os::raw::c_void,
673        name: *const ::std::os::raw::c_char,
674    ) {
675        raw::export_shared_api(self.ctx, func, name);
676    }
677
678    /// # Safety
679    ///
680    /// See [raw::notify_keyspace_event].
681    #[allow(clippy::must_use_candidate)]
682    pub fn notify_keyspace_event(
683        &self,
684        event_type: raw::NotifyEvent,
685        event: &str,
686        keyname: &RedisString,
687    ) -> raw::Status {
688        unsafe { raw::notify_keyspace_event(self.ctx, event_type, event, keyname) }
689    }
690
691    pub fn current_command_name(&self) -> Result<String, RedisError> {
692        unsafe {
693            match raw::RedisModule_GetCurrentCommandName {
694                Some(cmd) => Ok(CStr::from_ptr(cmd(self.ctx)).to_str().unwrap().to_string()),
695                None => Err(RedisError::Str(
696                    "API RedisModule_GetCurrentCommandName is not available",
697                )),
698            }
699        }
700    }
701
702    /// Returns the redis version either by calling `RedisModule_GetServerVersion` API,
703    /// Or if it is not available, by calling "info server" API and parsing the reply
704    pub fn get_redis_version(&self) -> Result<Version, RedisError> {
705        self.get_redis_version_internal(false)
706    }
707
708    /// Returns the redis version by calling "info server" API and parsing the reply
709    pub fn get_redis_version_rm_call(&self) -> Result<Version, RedisError> {
710        self.get_redis_version_internal(true)
711    }
712
713    pub fn version_from_info(info: RedisValue) -> Result<Version, RedisError> {
714        if let RedisValue::SimpleString(info_str) = info {
715            if let Some(ver) = utils::get_regexp_captures(
716                info_str.as_str(),
717                r"(?m)\bredis_version:([0-9]+)\.([0-9]+)\.([0-9]+)\b",
718            ) {
719                return Ok(Version {
720                    major: ver[0].parse::<c_int>().unwrap(),
721                    minor: ver[1].parse::<c_int>().unwrap(),
722                    patch: ver[2].parse::<c_int>().unwrap(),
723                });
724            }
725        }
726        Err(RedisError::Str("Error getting redis_version"))
727    }
728
729    #[allow(clippy::not_unsafe_ptr_arg_deref)]
730    fn get_redis_version_internal(&self, force_use_rm_call: bool) -> Result<Version, RedisError> {
731        match unsafe { raw::RedisModule_GetServerVersion } {
732            Some(api) if !force_use_rm_call => {
733                // Call existing API
734                Ok(Version::from(unsafe { api() }))
735            }
736            _ => {
737                // Call "info server"
738                if let Ok(info) = self.call("info", &["server"]) {
739                    Self::version_from_info(info)
740                } else {
741                    Err(RedisError::Str("Error calling \"info server\""))
742                }
743            }
744        }
745    }
746    pub fn set_module_options(&self, options: ModuleOptions) {
747        unsafe { raw::RedisModule_SetModuleOptions.unwrap()(self.ctx, options.bits()) };
748    }
749
750    /// Return ContextFlags object that allows to check properties related to the state of
751    /// the current Redis instance such as:
752    /// * Role (master/slave)
753    /// * Loading RDB/AOF
754    /// * Execution mode such as multi exec or Lua
755    pub fn get_flags(&self) -> ContextFlags {
756        ContextFlags::from_bits_truncate(unsafe {
757            raw::RedisModule_GetContextFlags.unwrap()(self.ctx)
758        })
759    }
760
761    /// Return the current user name attached to the context
762    pub fn get_current_user(&self) -> RedisString {
763        let user = unsafe { raw::RedisModule_GetCurrentUserName.unwrap()(self.ctx) };
764        RedisString::from_redis_module_string(ptr::null_mut(), user)
765    }
766
767    /// Attach the given user to the current context so each operation performed from
768    /// now on using this context will be validated againts this new user.
769    /// Return [ContextUserScope] which make sure to unset the user when freed and
770    /// can not outlive the current [Context].
771    pub fn authenticate_user(
772        &self,
773        user_name: &RedisString,
774    ) -> Result<ContextUserScope<'_>, RedisError> {
775        let user = unsafe { raw::RedisModule_GetModuleUserFromUserName.unwrap()(user_name.inner) };
776        if user.is_null() {
777            return Err(RedisError::Str("User does not exists or disabled"));
778        }
779        unsafe { raw::RedisModule_SetContextUser.unwrap()(self.ctx, user) };
780        Ok(ContextUserScope::new(self, user))
781    }
782
783    fn deautenticate_user(&self) {
784        unsafe { raw::RedisModule_SetContextUser.unwrap()(self.ctx, ptr::null_mut()) };
785    }
786
787    /// Verify the the given user has the give ACL permission on the given key.
788    /// Return Ok(()) if the user has the permissions or error (with relevant error message)
789    /// if the validation failed.
790    pub fn acl_check_key_permission(
791        &self,
792        user_name: &RedisString,
793        key_name: &RedisString,
794        permissions: &AclPermissions,
795    ) -> Result<(), RedisError> {
796        let user = unsafe { raw::RedisModule_GetModuleUserFromUserName.unwrap()(user_name.inner) };
797        if user.is_null() {
798            return Err(RedisError::Str("User does not exists or disabled"));
799        }
800        let acl_permission_result: raw::Status = unsafe {
801            raw::RedisModule_ACLCheckKeyPermissions.unwrap()(
802                user,
803                key_name.inner,
804                permissions.bits(),
805            )
806        }
807        .into();
808        unsafe { raw::RedisModule_FreeModuleUser.unwrap()(user) };
809        let acl_permission_result: Result<(), &str> = acl_permission_result.into();
810        acl_permission_result.map_err(|_e| RedisError::Str("User does not have permissions on key"))
811    }
812
813    api!(
814        [RedisModule_AddPostNotificationJob],
815        /// When running inside a key space notification callback, it is dangerous and highly discouraged to perform any write
816        /// operation. In order to still perform write actions in this scenario, Redis provides this API ([add_post_notification_job])
817        /// that allows to register a job callback which Redis will call when the following condition holds:
818        ///
819        /// 1. It is safe to perform any write operation.
820        /// 2. The job will be called atomically along side the key space notification.
821        ///
822        /// Notice, one job might trigger key space notifications that will trigger more jobs.
823        /// This raises a concerns of entering an infinite loops, we consider infinite loops
824        /// as a logical bug that need to be fixed in the module, an attempt to protect against
825        /// infinite loops by halting the execution could result in violation of the feature correctness
826        /// and so Redis will make no attempt to protect the module from infinite loops.
827        pub fn add_post_notification_job<F: FnOnce(&Context) + 'static>(
828            &self,
829            callback: F,
830        ) -> Status {
831            let callback = Box::into_raw(Box::new(Some(callback)));
832            unsafe {
833                RedisModule_AddPostNotificationJob(
834                    self.ctx,
835                    Some(post_notification_job::<F>),
836                    callback as *mut c_void,
837                    Some(post_notification_job_free_callback::<F>),
838                )
839            }
840            .into()
841        }
842    );
843
844    api!(
845        [RedisModule_AvoidReplicaTraffic],
846        /// Returns true if a client sent the CLIENT PAUSE command to the server or
847        /// if Redis Cluster does a manual failover, pausing the clients.
848        /// This is needed when we have a master with replicas, and want to write,
849        /// without adding further data to the replication channel, that the replicas
850        /// replication offset, match the one of the master. When this happens, it is
851        /// safe to failover the master without data loss.
852        ///
853        /// However modules may generate traffic by calling commands or directly send
854        /// data to the replication stream.
855        ///
856        /// So modules may want to try to avoid very heavy background work that has
857        /// the effect of creating data to the replication channel, when this function
858        /// returns true. This is mostly useful for modules that have background
859        /// garbage collection tasks, or that do writes and replicate such writes
860        /// periodically in timer callbacks or other periodic callbacks.
861        pub fn avoid_replication_traffic(&self) -> bool {
862            unsafe { RedisModule_AvoidReplicaTraffic() == 1 }
863        }
864    );
865
866    /// Return [Ok(true)] is the current Redis deployment is enterprise, otherwise [Ok(false)].
867    /// Return error in case it was not possible to determind the deployment.
868    fn is_enterprise_internal(&self) -> Result<bool, RedisError> {
869        let info_res = self.call("info", &["server"])?;
870        let info = match &info_res {
871            RedisValue::BulkRedisString(res) => res.try_as_str()?,
872            RedisValue::SimpleString(res) => res.as_str(),
873            _ => return Err(RedisError::Str("Mismatch call reply type")),
874        };
875        Ok(info.contains("rlec_version:"))
876    }
877
878    /// Return `true` is the current Redis deployment is enterprise, otherwise `false`.
879    pub fn is_enterprise(&self) -> bool {
880        self.is_enterprise_internal().unwrap_or_else(|e| {
881            log::error!("Failed getting deployment type, assuming oss. Error: {e}.");
882            false
883        })
884    }
885}
886
887extern "C" fn post_notification_job_free_callback<F: FnOnce(&Context)>(pd: *mut c_void) {
888    drop(unsafe { Box::from_raw(pd as *mut Option<F>) });
889}
890
891extern "C" fn post_notification_job<F: FnOnce(&Context)>(
892    ctx: *mut raw::RedisModuleCtx,
893    pd: *mut c_void,
894) {
895    let callback = unsafe { &mut *(pd as *mut Option<F>) };
896    let ctx = Context::new(ctx);
897    callback.take().map_or_else(
898        || {
899            ctx.log(
900                RedisLogLevel::Warning,
901                "Got a None callback on post notification job.",
902            )
903        },
904        |callback| {
905            callback(&ctx);
906        },
907    );
908}
909
910unsafe impl RedisLockIndicator for Context {}
911
912bitflags! {
913    /// An object represent ACL permissions.
914    /// Used to check ACL permission using `acl_check_key_permission`.
915    #[derive(Debug)]
916    pub struct AclPermissions : c_int {
917        /// User can look at the content of the value, either return it or copy it.
918        const ACCESS = raw::REDISMODULE_CMD_KEY_ACCESS as c_int;
919
920        /// User can insert more data to the key, without deleting or modify existing data.
921        const INSERT = raw::REDISMODULE_CMD_KEY_INSERT as c_int;
922
923        /// User can delete content from the key.
924        const DELETE = raw::REDISMODULE_CMD_KEY_DELETE as c_int;
925
926        /// User can update existing data inside the key.
927        const UPDATE = raw::REDISMODULE_CMD_KEY_UPDATE as c_int;
928    }
929}
930
931/// The values allowed in the "info" sections and dictionaries.
932#[derive(Debug, Clone)]
933pub enum InfoContextBuilderFieldBottomLevelValue {
934    /// A simple string value.
935    String(String),
936    /// A numeric value ([`i64`]).
937    I64(i64),
938    /// A numeric value ([`u64`]).
939    U64(u64),
940    /// A numeric value ([`f64`]).
941    F64(f64),
942}
943
944impl From<String> for InfoContextBuilderFieldBottomLevelValue {
945    fn from(value: String) -> Self {
946        Self::String(value)
947    }
948}
949
950impl From<&str> for InfoContextBuilderFieldBottomLevelValue {
951    fn from(value: &str) -> Self {
952        Self::String(value.to_owned())
953    }
954}
955
956impl From<i64> for InfoContextBuilderFieldBottomLevelValue {
957    fn from(value: i64) -> Self {
958        Self::I64(value)
959    }
960}
961
962impl From<u64> for InfoContextBuilderFieldBottomLevelValue {
963    fn from(value: u64) -> Self {
964        Self::U64(value)
965    }
966}
967
968#[derive(Debug, Clone)]
969pub enum InfoContextBuilderFieldTopLevelValue {
970    /// A simple bottom-level value.
971    Value(InfoContextBuilderFieldBottomLevelValue),
972    /// A dictionary value.
973    ///
974    /// An example of what it looks like:
975    /// ```no_run,ignore,
976    /// > redis-cli: INFO
977    /// >
978    /// > # <section name>
979    /// <dictionary name>:<key 1>=<value 1>,<key 2>=<value 2>
980    /// ```
981    ///
982    /// Let's suppose we added a section `"my_info"`. Then into this
983    /// section we can add a dictionary. Let's add a dictionary named
984    /// `"module"`, with with fields `"name"` which is equal to
985    /// `"redisgears_2"` and `"ver"` with a value of `999999`. If our
986    /// module is named "redisgears_2", we can call `INFO redisgears_2`
987    /// to obtain this information:
988    ///
989    /// ```no_run,ignore,
990    /// > redis-cli: INFO redisgears_2
991    /// >
992    /// > # redisgears_2_my_info
993    /// module:name=redisgears_2,ver=999999
994    /// ```
995    Dictionary {
996        name: String,
997        fields: InfoContextFieldBottomLevelData,
998    },
999}
1000
1001impl<T: Into<InfoContextBuilderFieldBottomLevelValue>> From<T>
1002    for InfoContextBuilderFieldTopLevelValue
1003{
1004    fn from(value: T) -> Self {
1005        Self::Value(value.into())
1006    }
1007}
1008
1009/// Builds a dictionary within the [`InfoContext`], similar to
1010/// `INFO KEYSPACE`.
1011#[derive(Debug)]
1012pub struct InfoContextBuilderDictionaryBuilder<'a> {
1013    /// The info section builder this dictionary builder is for.
1014    info_section_builder: InfoContextBuilderSectionBuilder<'a>,
1015    /// The name of the section to build.
1016    name: String,
1017    /// The fields this section contains.
1018    fields: InfoContextFieldBottomLevelData,
1019}
1020
1021impl<'a> InfoContextBuilderDictionaryBuilder<'a> {
1022    /// Adds a field within this section.
1023    pub fn field<F: Into<InfoContextBuilderFieldBottomLevelValue>>(
1024        mut self,
1025        name: &str,
1026        value: F,
1027    ) -> RedisResult<Self> {
1028        if self.fields.iter().any(|k| k.0 .0 == name) {
1029            return Err(RedisError::String(format!(
1030                "Found duplicate key '{name}' in the info dictionary '{}'",
1031                self.name
1032            )));
1033        }
1034
1035        self.fields.push((name.to_owned(), value.into()).into());
1036        Ok(self)
1037    }
1038
1039    /// Builds the dictionary with the fields provided.
1040    pub fn build_dictionary(self) -> RedisResult<InfoContextBuilderSectionBuilder<'a>> {
1041        let name = self.name;
1042        let name_ref = name.clone();
1043        self.info_section_builder.field(
1044            &name_ref,
1045            InfoContextBuilderFieldTopLevelValue::Dictionary {
1046                name,
1047                fields: self.fields.to_owned(),
1048            },
1049        )
1050    }
1051}
1052
1053/// Builds a section within the [`InfoContext`].
1054#[derive(Debug)]
1055pub struct InfoContextBuilderSectionBuilder<'a> {
1056    /// The info builder this section builder is for.
1057    info_builder: InfoContextBuilder<'a>,
1058    /// The name of the section to build.
1059    name: String,
1060    /// The fields this section contains.
1061    fields: InfoContextFieldTopLevelData,
1062}
1063
1064impl<'a> InfoContextBuilderSectionBuilder<'a> {
1065    /// Adds a field within this section.
1066    pub fn field<F: Into<InfoContextBuilderFieldTopLevelValue>>(
1067        mut self,
1068        name: &str,
1069        value: F,
1070    ) -> RedisResult<Self> {
1071        if self.fields.iter().any(|(k, _)| k == name) {
1072            return Err(RedisError::String(format!(
1073                "Found duplicate key '{name}' in the info section '{}'",
1074                self.name
1075            )));
1076        }
1077        self.fields.push((name.to_owned(), value.into()));
1078        Ok(self)
1079    }
1080
1081    /// Adds a new dictionary.
1082    pub fn add_dictionary(self, dictionary_name: &str) -> InfoContextBuilderDictionaryBuilder<'a> {
1083        InfoContextBuilderDictionaryBuilder {
1084            info_section_builder: self,
1085            name: dictionary_name.to_owned(),
1086            fields: InfoContextFieldBottomLevelData::default(),
1087        }
1088    }
1089
1090    /// Builds the section with the fields provided.
1091    pub fn build_section(mut self) -> RedisResult<InfoContextBuilder<'a>> {
1092        if self
1093            .info_builder
1094            .sections
1095            .iter()
1096            .any(|(k, _)| k == &self.name)
1097        {
1098            return Err(RedisError::String(format!(
1099                "Found duplicate section in the Info reply: {}",
1100                self.name
1101            )));
1102        }
1103
1104        self.info_builder
1105            .sections
1106            .push((self.name.clone(), self.fields));
1107
1108        Ok(self.info_builder)
1109    }
1110}
1111
1112/// A single info context's bottom level field data.
1113#[derive(Debug, Clone)]
1114#[repr(transparent)]
1115pub struct InfoContextBottomLevelFieldData(pub (String, InfoContextBuilderFieldBottomLevelValue));
1116impl Deref for InfoContextBottomLevelFieldData {
1117    type Target = (String, InfoContextBuilderFieldBottomLevelValue);
1118
1119    fn deref(&self) -> &Self::Target {
1120        &self.0
1121    }
1122}
1123impl std::ops::DerefMut for InfoContextBottomLevelFieldData {
1124    fn deref_mut(&mut self) -> &mut Self::Target {
1125        &mut self.0
1126    }
1127}
1128
1129impl<T: Into<InfoContextBuilderFieldBottomLevelValue>> From<(String, T)>
1130    for InfoContextBottomLevelFieldData
1131{
1132    fn from(value: (String, T)) -> Self {
1133        Self((value.0, value.1.into()))
1134    }
1135}
1136/// A type for the `key => bottom-level-value` storage of an info
1137/// section.
1138#[derive(Debug, Default, Clone)]
1139#[repr(transparent)]
1140pub struct InfoContextFieldBottomLevelData(pub Vec<InfoContextBottomLevelFieldData>);
1141impl Deref for InfoContextFieldBottomLevelData {
1142    type Target = Vec<InfoContextBottomLevelFieldData>;
1143
1144    fn deref(&self) -> &Self::Target {
1145        &self.0
1146    }
1147}
1148impl std::ops::DerefMut for InfoContextFieldBottomLevelData {
1149    fn deref_mut(&mut self) -> &mut Self::Target {
1150        &mut self.0
1151    }
1152}
1153
1154/// A type alias for the `key => top-level-value` storage of an info
1155/// section.
1156pub type InfoContextFieldTopLevelData = Vec<(String, InfoContextBuilderFieldTopLevelValue)>;
1157/// One section contents: name and children.
1158pub type OneInfoSectionData = (String, InfoContextFieldTopLevelData);
1159/// A type alias for the section data, associated with the info section.
1160pub type InfoContextTreeData = Vec<OneInfoSectionData>;
1161
1162impl<T: Into<InfoContextBuilderFieldBottomLevelValue>> From<BTreeMap<String, T>>
1163    for InfoContextFieldBottomLevelData
1164{
1165    fn from(value: BTreeMap<String, T>) -> Self {
1166        Self(
1167            value
1168                .into_iter()
1169                .map(|e| (e.0, e.1.into()).into())
1170                .collect(),
1171        )
1172    }
1173}
1174
1175impl<T: Into<InfoContextBuilderFieldBottomLevelValue>> From<HashMap<String, T>>
1176    for InfoContextFieldBottomLevelData
1177{
1178    fn from(value: HashMap<String, T>) -> Self {
1179        Self(
1180            value
1181                .into_iter()
1182                .map(|e| (e.0, e.1.into()).into())
1183                .collect(),
1184        )
1185    }
1186}
1187
1188#[derive(Debug)]
1189pub struct InfoContextBuilder<'a> {
1190    context: &'a InfoContext,
1191    sections: InfoContextTreeData,
1192}
1193impl<'a> InfoContextBuilder<'a> {
1194    fn add_bottom_level_field(
1195        &self,
1196        key: &str,
1197        value: &InfoContextBuilderFieldBottomLevelValue,
1198    ) -> RedisResult<()> {
1199        use InfoContextBuilderFieldBottomLevelValue as BottomLevel;
1200
1201        match value {
1202            BottomLevel::String(string) => add_info_field_str(self.context.ctx, key, string),
1203            BottomLevel::I64(number) => add_info_field_long_long(self.context.ctx, key, *number),
1204            BottomLevel::U64(number) => {
1205                add_info_field_unsigned_long_long(self.context.ctx, key, *number)
1206            }
1207            BottomLevel::F64(number) => add_info_field_double(self.context.ctx, key, *number),
1208        }
1209        .into()
1210    }
1211    /// Adds fields. Make sure that the corresponding section/dictionary
1212    /// have been added before calling this method.
1213    fn add_top_level_fields(&self, fields: &InfoContextFieldTopLevelData) -> RedisResult<()> {
1214        use InfoContextBuilderFieldTopLevelValue as TopLevel;
1215
1216        fields.iter().try_for_each(|(key, value)| match value {
1217            TopLevel::Value(bottom_level) => self.add_bottom_level_field(key, bottom_level),
1218            TopLevel::Dictionary { name, fields } => {
1219                std::convert::Into::<RedisResult<()>>::into(add_info_begin_dict_field(
1220                    self.context.ctx,
1221                    name,
1222                ))?;
1223                fields
1224                    .iter()
1225                    .try_for_each(|f| self.add_bottom_level_field(&f.0 .0, &f.0 .1))?;
1226                add_info_end_dict_field(self.context.ctx).into()
1227            }
1228        })
1229    }
1230
1231    fn finalise_data(&self) -> RedisResult<()> {
1232        self.sections
1233            .iter()
1234            .try_for_each(|(section_name, section_fields)| -> RedisResult<()> {
1235                if add_info_section(self.context.ctx, Some(section_name)) == Status::Ok {
1236                    self.add_top_level_fields(section_fields)
1237                } else {
1238                    // This section wasn't requested.
1239                    Ok(())
1240                }
1241            })
1242    }
1243
1244    /// Sends the info accumulated so far to the [`InfoContext`].
1245    pub fn build_info(self) -> RedisResult<&'a InfoContext> {
1246        self.finalise_data().map(|_| self.context)
1247    }
1248
1249    /// Returns a section builder.
1250    pub fn add_section(self, name: &'a str) -> InfoContextBuilderSectionBuilder<'a> {
1251        InfoContextBuilderSectionBuilder {
1252            info_builder: self,
1253            name: name.to_owned(),
1254            fields: InfoContextFieldTopLevelData::new(),
1255        }
1256    }
1257
1258    /// Adds the section data without checks for the values already
1259    /// being present. In this case, the values will be overwritten.
1260    pub(crate) fn add_section_unchecked(mut self, section: OneInfoSectionData) -> Self {
1261        self.sections.push(section);
1262        self
1263    }
1264}
1265
1266impl<'a> From<&'a InfoContext> for InfoContextBuilder<'a> {
1267    fn from(context: &'a InfoContext) -> Self {
1268        Self {
1269            context,
1270            sections: InfoContextTreeData::new(),
1271        }
1272    }
1273}
1274
1275#[derive(Debug)]
1276pub struct InfoContext {
1277    pub ctx: *mut raw::RedisModuleInfoCtx,
1278}
1279
1280impl InfoContext {
1281    pub const fn new(ctx: *mut raw::RedisModuleInfoCtx) -> Self {
1282        Self { ctx }
1283    }
1284
1285    /// Returns a builder for the [`InfoContext`].
1286    pub fn builder(&self) -> InfoContextBuilder<'_> {
1287        InfoContextBuilder::from(self)
1288    }
1289
1290    /// Returns a build result for the passed [`OneInfoSectionData`].
1291    pub fn build_one_section<T: Into<OneInfoSectionData>>(&self, data: T) -> RedisResult<()> {
1292        self.builder()
1293            .add_section_unchecked(data.into())
1294            .build_info()?;
1295        Ok(())
1296    }
1297
1298    #[deprecated = "Please use [`InfoContext::builder`] instead."]
1299    /// The `name` of the sction will be prefixed with the module name
1300    /// and an underscore: `<module name>_<name>`.
1301    pub fn add_info_section(&self, name: Option<&str>) -> Status {
1302        add_info_section(self.ctx, name)
1303    }
1304
1305    #[deprecated = "Please use [`InfoContext::builder`] instead."]
1306    /// The `name` will be prefixed with the module name and an
1307    /// underscore: `<module name>_<name>`. The `content` pass is left
1308    /// "as is".
1309    pub fn add_info_field_str(&self, name: &str, content: &str) -> Status {
1310        add_info_field_str(self.ctx, name, content)
1311    }
1312
1313    #[deprecated = "Please use [`InfoContext::builder`] instead."]
1314    /// The `name` will be prefixed with the module name and an
1315    /// underscore: `<module name>_<name>`. The `value` pass is left
1316    /// "as is".
1317    pub fn add_info_field_long_long(&self, name: &str, value: c_longlong) -> Status {
1318        add_info_field_long_long(self.ctx, name, value)
1319    }
1320}
1321
1322bitflags! {
1323    pub struct ContextFlags : c_int {
1324        /// The command is running in the context of a Lua script
1325        const LUA = raw::REDISMODULE_CTX_FLAGS_LUA as c_int;
1326
1327        /// The command is running inside a Redis transaction
1328        const MULTI = raw::REDISMODULE_CTX_FLAGS_MULTI as c_int;
1329
1330        /// The instance is a master
1331        const MASTER = raw::REDISMODULE_CTX_FLAGS_MASTER as c_int;
1332
1333        /// The instance is a SLAVE
1334        const SLAVE = raw::REDISMODULE_CTX_FLAGS_SLAVE as c_int;
1335
1336        /// The instance is read-only (usually meaning it's a slave as well)
1337        const READONLY = raw::REDISMODULE_CTX_FLAGS_READONLY as c_int;
1338
1339        /// The instance is running in cluster mode
1340        const CLUSTER = raw::REDISMODULE_CTX_FLAGS_CLUSTER as c_int;
1341
1342        /// The instance has AOF enabled
1343        const AOF = raw::REDISMODULE_CTX_FLAGS_AOF as c_int;
1344
1345        /// The instance has RDB enabled
1346        const RDB = raw::REDISMODULE_CTX_FLAGS_RDB as c_int;
1347
1348        /// The instance has Maxmemory set
1349        const MAXMEMORY = raw::REDISMODULE_CTX_FLAGS_MAXMEMORY as c_int;
1350
1351        /// Maxmemory is set and has an eviction policy that may delete keys
1352        const EVICTED = raw::REDISMODULE_CTX_FLAGS_EVICT as c_int;
1353
1354        /// Redis is out of memory according to the maxmemory flag.
1355        const OOM = raw::REDISMODULE_CTX_FLAGS_OOM as c_int;
1356
1357        /// Less than 25% of memory available according to maxmemory.
1358        const OOM_WARNING = raw::REDISMODULE_CTX_FLAGS_OOM_WARNING as c_int;
1359
1360        /// The command was sent over the replication link.
1361        const REPLICATED = raw::REDISMODULE_CTX_FLAGS_REPLICATED as c_int;
1362
1363        /// Redis is currently loading either from AOF or RDB.
1364        const LOADING = raw::REDISMODULE_CTX_FLAGS_LOADING as c_int;
1365
1366        /// The replica has no link with its master
1367        const REPLICA_IS_STALE = raw::REDISMODULE_CTX_FLAGS_REPLICA_IS_STALE as c_int;
1368
1369        /// The replica is trying to connect with the master
1370        const REPLICA_IS_CONNECTING = raw::REDISMODULE_CTX_FLAGS_REPLICA_IS_CONNECTING as c_int;
1371
1372        /// The replica is receiving an RDB file from its master.
1373        const REPLICA_IS_TRANSFERRING = raw::REDISMODULE_CTX_FLAGS_REPLICA_IS_TRANSFERRING as c_int;
1374
1375        /// The replica is online, receiving updates from its master
1376        const REPLICA_IS_ONLINE = raw::REDISMODULE_CTX_FLAGS_REPLICA_IS_ONLINE as c_int;
1377
1378        /// There is currently some background process active.
1379        const ACTIVE_CHILD = raw::REDISMODULE_CTX_FLAGS_ACTIVE_CHILD as c_int;
1380
1381        /// Redis is currently running inside background child process.
1382        const IS_CHILD = raw::REDISMODULE_CTX_FLAGS_IS_CHILD as c_int;
1383
1384        /// The next EXEC will fail due to dirty CAS (touched keys).
1385        const MULTI_DIRTY = raw::REDISMODULE_CTX_FLAGS_MULTI_DIRTY as c_int;
1386
1387        /// The current client does not allow blocking, either called from
1388        /// within multi, lua, or from another module using RM_Call
1389        const DENY_BLOCKING = raw::REDISMODULE_CTX_FLAGS_DENY_BLOCKING as c_int;
1390
1391        /// The current client uses RESP3 protocol
1392        const FLAGS_RESP3 = raw::REDISMODULE_CTX_FLAGS_RESP3 as c_int;
1393
1394        /// Redis is currently async loading database for diskless replication.
1395        const ASYNC_LOADING = raw::REDISMODULE_CTX_FLAGS_ASYNC_LOADING as c_int;
1396    }
1397}