redis_driver/commands/
scripting_commands.rs

1use std::collections::HashMap;
2
3use crate::{
4    prepare_command,
5    resp::{
6        cmd, CommandArg, CommandArgs, FromValue, IntoArgs, SingleArgOrCollection, Value,
7    },
8    Error, FlushingMode, PreparedCommand, Result,
9};
10
11/// A group of Redis commands related to Scripting and Functions
12/// # See Also
13/// [Redis Scripting and Functions Commands](https://redis.io/commands/?group=scripting)
14/// [Scripting with LUA](https://redis.io/docs/manual/programmability/eval-intro/)
15/// [Functions](https://redis.io/docs/manual/programmability/functions-intro/)
16pub trait ScriptingCommands {
17    /// Invoke the execution of a server-side Lua script.
18    ///
19    /// # Return
20    /// The return value of the script
21    ///
22    /// # See Also
23    /// [<https://redis.io/commands/eval/>](https://redis.io/commands/eval/)
24    #[must_use]
25    fn eval<R>(&mut self, builder: CallBuilder) -> PreparedCommand<Self, R>
26    where
27        Self: Sized,
28        R: FromValue,
29    {
30        prepare_command(self, cmd("EVAL").arg(builder))
31    }
32
33    /// This is a read-only variant of the [eval](crate::ScriptingCommands::eval)]
34    /// command that cannot execute commands that modify data.
35    ///
36    /// # Return
37    /// The return value of the script
38    ///
39    /// # See Also
40    /// [<https://redis.io/commands/eval_ro/>](https://redis.io/commands/eval_ro/)
41    #[must_use]
42    fn eval_readonly<R>(&mut self, builder: CallBuilder) -> PreparedCommand<Self, R>
43    where
44        Self: Sized,
45        R: FromValue,
46    {
47        prepare_command(self, cmd("EVAL_RO").arg(builder))
48    }
49
50    /// Evaluate a script from the server's cache by its SHA1 digest.
51    ///
52    /// # Return
53    /// The return value of the script
54    ///
55    /// # See Also
56    /// [<https://redis.io/commands/eval/>](https://redis.io/commands/eval/)
57    #[must_use]
58    fn evalsha<R>(&mut self, builder: CallBuilder) -> PreparedCommand<Self, R>
59    where
60        Self: Sized,
61        R: FromValue,
62    {
63        prepare_command(self, cmd("EVALSHA").arg(builder))
64    }
65
66    /// This is a read-only variant of the [evalsha](crate::ScriptingCommands::evalsha)
67    /// command that cannot execute commands that modify data.
68    ///
69    /// # Return
70    /// The return value of the script
71    ///
72    /// # See Also
73    /// [<https://redis.io/commands/evalsha_ro/>](https://redis.io/commands/evalsha_ro/)
74    #[must_use]
75    fn evalsha_readonly<R>(&mut self, builder: CallBuilder) -> PreparedCommand<Self, R>
76    where
77        Self: Sized,
78        R: FromValue,
79    {
80        prepare_command(self, cmd("EVALSHA_RO").arg(builder))
81    }
82
83    /// Invoke a function.
84    ///
85    /// # Return
86    /// The return value of the function
87    ///
88    /// # See Also
89    /// [<https://redis.io/commands/fcall/>](https://redis.io/commands/fcall/)
90    #[must_use]
91    fn fcall<R>(&mut self, builder: CallBuilder) -> PreparedCommand<Self, R>
92    where
93        Self: Sized,
94        R: FromValue,
95    {
96        prepare_command(self, cmd("FCALL").arg(builder))
97    }
98
99    /// Invoke a function.
100    ///
101    /// # Return
102    /// The return value of the function
103    ///
104    /// # See Also
105    /// [<https://redis.io/commands/fcall-ro/>](https://redis.io/commands/fcall_ro/)
106    #[must_use]
107    fn fcall_readonly<R>(&mut self, builder: CallBuilder) -> PreparedCommand<Self, R>
108    where
109        Self: Sized,
110        R: FromValue,
111    {
112        prepare_command(self, cmd("FCALL_RO").arg(builder))
113    }
114
115    /// Delete a library and all its functions.
116    ///
117    /// # See Also
118    /// [<https://redis.io/commands/function-delete/>](https://redis.io/commands/function-delete/)
119    #[must_use]
120    fn function_delete<L>(&mut self, library_name: L) -> PreparedCommand<Self, ()>
121    where
122        Self: Sized,
123        L: Into<CommandArg>,
124    {
125        prepare_command(self, cmd("FUNCTION").arg("DELETE").arg(library_name))
126    }
127
128    /// Return the serialized payload of loaded libraries.
129    /// You can restore the serialized payload later with the
130    /// [`function_restore`](crate::ScriptingCommands::function_restore) command.
131    ///
132    /// # Return
133    /// The serialized payload
134    ///
135    /// # See Also
136    /// [<https://redis.io/commands/function-dump/>](https://redis.io/commands/function-dump/)
137    #[must_use]
138    fn function_dump<P>(&mut self) -> PreparedCommand<Self, P>
139    where
140        Self: Sized,
141        P: FromValue,
142    {
143        prepare_command(self, cmd("FUNCTION").arg("DUMP"))
144    }
145
146    /// Deletes all the libraries.
147    ///
148    /// # See Also
149    /// [<https://redis.io/commands/function-flush/>](https://redis.io/commands/function-flush/)
150    #[must_use]
151    fn function_flush(&mut self, flushing_mode: FlushingMode) -> PreparedCommand<Self, ()>
152    where
153        Self: Sized,
154    {
155        prepare_command(self, cmd("FUNCTION").arg("FLUSH").arg(flushing_mode))
156    }
157
158    /// Kill a function that is currently executing.
159    ///
160    /// # See Also
161    /// [<https://redis.io/commands/function-kill/>](https://redis.io/commands/function-kill/)
162    #[must_use]
163    fn function_kill(&mut self) -> PreparedCommand<Self, ()>
164    where
165        Self: Sized,
166    {
167        prepare_command(self, cmd("FUNCTION").arg("KILL"))
168    }
169
170    /// Return information about the functions and libraries.
171    ///
172    /// # See Also
173    /// [<https://redis.io/commands/function-list/>](https://redis.io/commands/function-list/)
174    #[must_use]
175    fn function_list(
176        &mut self,
177        options: FunctionListOptions,
178    ) -> PreparedCommand<Self, Vec<LibraryInfo>>
179    where
180        Self: Sized,
181    {
182        prepare_command(self, cmd("FUNCTION").arg("LIST").arg(options))
183    }
184
185    /// Load a library to Redis.
186    ///
187    /// # Return
188    /// The library name that was loaded
189    ///
190    /// # See Also
191    /// [<https://redis.io/commands/function-load/>](https://redis.io/commands/function-load/)
192    #[must_use]
193    fn function_load<F, L>(&mut self, replace: bool, function_code: F) -> PreparedCommand<Self, L>
194    where
195        Self: Sized,
196        F: Into<CommandArg>,
197        L: FromValue,
198    {
199        prepare_command(
200            self,
201            cmd("FUNCTION")
202                .arg("LOAD")
203                .arg_if(replace, "REPLACE")
204                .arg(function_code),
205        )
206    }
207
208    /// Restore libraries from the serialized payload.
209    ///
210    /// # See Also
211    /// [<https://redis.io/commands/function-restore/>](https://redis.io/commands/function-restore/)
212    #[must_use]
213    fn function_restore<P>(
214        &mut self,
215        serialized_payload: P,
216        policy: FunctionRestorePolicy,
217    ) -> PreparedCommand<Self, ()>
218    where
219        Self: Sized,
220        P: Into<CommandArg>,
221    {
222        prepare_command(
223            self,
224            cmd("FUNCTION")
225                .arg("RESTORE")
226                .arg(serialized_payload)
227                .arg(policy),
228        )
229    }
230
231    /// Return information about the function that's currently running and information about the available execution engines.
232    ///
233    /// # See Also
234    /// [<https://redis.io/commands/function-stats/>](https://redis.io/commands/function-stats/)
235    #[must_use]
236    fn function_stats(&mut self) -> PreparedCommand<Self, FunctionStats>
237    where
238        Self: Sized,
239    {
240        prepare_command(self, cmd("FUNCTION").arg("STATS"))
241    }
242
243    /// Set the debug mode for subsequent scripts executed with EVAL.
244    ///
245    /// # See Also
246    /// [<https://redis.io/commands/script-debug/>](https://redis.io/commands/script-debug/)
247    #[must_use]
248    fn script_debug(&mut self, debug_mode: ScriptDebugMode) -> PreparedCommand<Self, ()>
249    where
250        Self: Sized,
251    {
252        prepare_command(self, cmd("SCRIPT").arg("DEBUG").arg(debug_mode))
253    }
254
255    /// Returns information about the existence of the scripts in the script cache.
256    ///
257    /// # Return
258    /// The SHA1 digest of the script added into the script cache.
259    ///
260    /// # See Also
261    /// [<https://redis.io/commands/script-exists/>](https://redis.io/commands/script-exists/)
262    #[must_use]
263    fn script_exists<S, C>(&mut self, sha1s: C) -> PreparedCommand<Self, Vec<bool>>
264    where
265        Self: Sized,
266        S: Into<CommandArg>,
267        C: SingleArgOrCollection<S>,
268    {
269        prepare_command(self, cmd("SCRIPT").arg("EXISTS").arg(sha1s))
270    }
271
272    /// Flush the Lua scripts cache.
273    ///
274    /// # See Also
275    /// [<https://redis.io/commands/script-flush/>](https://redis.io/commands/script-flush/)
276    #[must_use]
277    fn script_flush(&mut self, flushing_mode: FlushingMode) -> PreparedCommand<Self, ()>
278    where
279        Self: Sized,
280    {
281        prepare_command(self, cmd("SCRIPT").arg("FLUSH").arg(flushing_mode))
282    }
283
284    /// Kills the currently executing EVAL script,
285    /// assuming no write operation was yet performed by the script.
286    ///
287    /// # See Also
288    /// [<https://redis.io/commands/script-kill/>](https://redis.io/commands/script-kill/)
289    #[must_use]
290    fn script_kill(&mut self) -> PreparedCommand<Self, ()>
291    where
292        Self: Sized,
293    {
294        prepare_command(self, cmd("SCRIPT").arg("KILL"))
295    }
296
297    /// Load a script into the scripts cache, without executing it.
298    ///
299    /// # Return
300    /// The SHA1 digest of the script added into the script cache.
301    ///
302    /// # See Also
303    /// [<https://redis.io/commands/script-load/>](https://redis.io/commands/script-load/)
304    #[must_use]
305    fn script_load<S, V>(&mut self, script: S) -> PreparedCommand<Self, V>
306    where
307        Self: Sized,
308        S: Into<CommandArg>,
309        V: FromValue,
310    {
311        prepare_command(self, cmd("SCRIPT").arg("LOAD").arg(script))
312    }
313}
314
315/// Builder for calling a script/function for the following commands:
316/// * [`eval`](crate::ScriptingCommands::eval)
317/// * [`eval_readonly`](crate::ScriptingCommands::eval_readonly)
318/// * [`eval_sha`](crate::ScriptingCommands::evalsha)
319/// * [`evalsha_readonly`](crate::ScriptingCommands::evalsha_readonly)
320/// * [`fcall`](crate::ScriptingCommands::fcall)
321/// * [`fcall_readonly`](crate::ScriptingCommands::fcall_readonly)
322pub struct CallBuilder {
323    command_args: CommandArgs,
324    keys_added: bool,
325}
326
327impl CallBuilder {
328    #[must_use]
329    pub fn script<S: Into<CommandArg>>(script: S) -> Self {
330        Self {
331            command_args: CommandArgs::default().arg(script),
332            keys_added: false,
333        }
334    }
335
336    #[must_use]
337    pub fn sha1<S: Into<CommandArg>>(sha1: S) -> Self {
338        Self {
339            command_args: CommandArgs::default().arg(sha1),
340            keys_added: false,
341        }
342    }
343
344    #[must_use]
345    pub fn function<F: Into<CommandArg>>(function: F) -> Self {
346        Self {
347            command_args: CommandArgs::default().arg(function),
348            keys_added: false,
349        }
350    }
351
352    /// All the keys accessed by the script.
353    #[must_use]
354    pub fn keys<K, C>(self, keys: C) -> Self
355    where
356        K: Into<CommandArg>,
357        C: SingleArgOrCollection<K>,
358    {
359        Self {
360            command_args: self.command_args.arg(keys.num_args()).arg(keys),
361            keys_added: true,
362        }
363    }
364
365    /// Additional input arguments that should not represent names of keys.
366    #[must_use]
367    pub fn args<A, C>(self, args: C) -> Self
368    where
369        A: Into<CommandArg>,
370        C: SingleArgOrCollection<A>,
371    {
372        let command_args = if self.keys_added {
373            self.command_args.arg(args)
374        } else {
375            // numkeys = 0
376            self.command_args.arg(0).arg(args)
377        };
378
379        Self {
380            command_args,
381            keys_added: true,
382        }
383    }
384}
385
386impl IntoArgs for CallBuilder {
387    fn into_args(self, args: CommandArgs) -> CommandArgs {
388        // no keys, no args
389        if self.command_args.len() == 1 {
390            args.arg(self.command_args).arg(0)
391        } else {
392            args.arg(self.command_args)
393        }
394    }
395}
396
397/// Policy option for the [`function_restore`](crate::ScriptingCommands::function_restore) command.
398pub enum FunctionRestorePolicy {
399    /// Append
400    Default,
401    /// Appends the restored libraries to the existing libraries and aborts on collision.
402    /// This is the default policy.
403    Append,
404    /// Deletes all existing libraries before restoring the payload.
405    Flush,
406    /// appends the restored libraries to the existing libraries,
407    /// replacing any existing ones in case of name collisions.
408    /// Note that this policy doesn't prevent function name collisions, only libraries.
409    Replace,
410}
411
412impl Default for FunctionRestorePolicy {
413    fn default() -> Self {
414        Self::Default
415    }
416}
417
418impl IntoArgs for FunctionRestorePolicy {
419    fn into_args(self, args: CommandArgs) -> CommandArgs {
420        match self {
421            FunctionRestorePolicy::Default => args,
422            FunctionRestorePolicy::Append => args.arg("APPEND"),
423            FunctionRestorePolicy::Flush => args.arg("FLUSH"),
424            FunctionRestorePolicy::Replace => args.arg("REPLACE"),
425        }
426    }
427}
428
429#[derive(Debug)]
430pub struct LibraryInfo {
431    pub library_name: String,
432    pub engine: String,
433    pub functions: Vec<FunctionInfo>,
434    pub library_code: Option<String>,
435}
436
437impl FromValue for LibraryInfo {
438    fn from_value(value: Value) -> Result<Self> {
439        match &value {
440            Value::Array(Some(v)) if v.len() == 8 => {
441                fn into_result(values: &mut HashMap<String, Value>) -> Option<LibraryInfo> {
442                    Some(LibraryInfo {
443                        library_name: values.remove("library_name")?.into().ok()?,
444                        engine: values.remove("engine")?.into().ok()?,
445                        functions: values.remove("functions")?.into().ok()?,
446                        library_code: values.remove("library_code")?.into().ok()?,
447                    })
448                }
449
450                into_result(&mut value.into()?)
451                    .ok_or_else(|| Error::Client("Cannot parse LibraryInfo".to_owned()))
452            }
453            _ => {
454                fn into_result(values: &mut HashMap<String, Value>) -> Option<LibraryInfo> {
455                    Some(LibraryInfo {
456                        library_name: values.remove("library_name")?.into().ok()?,
457                        engine: values.remove("engine")?.into().ok()?,
458                        functions: values.remove("functions")?.into().ok()?,
459                        library_code: None,
460                    })
461                }
462
463                into_result(&mut value.into()?)
464                    .ok_or_else(|| Error::Client("Cannot parse LibraryInfo".to_owned()))
465            }
466        }
467    }
468}
469
470#[derive(Debug)]
471pub struct FunctionInfo {
472    pub name: String,
473    pub description: String,
474    pub flags: Vec<String>,
475}
476
477impl FromValue for FunctionInfo {
478    fn from_value(value: Value) -> Result<Self> {
479        match &value {
480            Value::Array(Some(v)) if v.len() == 6 => {
481                fn into_result(values: &mut HashMap<String, Value>) -> Option<FunctionInfo> {
482                    Some(FunctionInfo {
483                        name: values.remove("name")?.into().ok()?,
484                        description: values.remove("description")?.into().ok()?,
485                        flags: values.remove("flags")?.into().ok()?,
486                    })
487                }
488
489                into_result(&mut value.into()?)
490                    .ok_or_else(|| Error::Client("Cannot parse FunctionInfo".to_owned()))
491            }
492            _ => Err(Error::Client("Cannot parse FunctionInfo".to_owned())),
493        }
494    }
495}
496
497#[derive(Debug)]
498pub struct FunctionStats {
499    pub running_script: Option<RunningScript>,
500    pub engines: HashMap<String, EngineStats>,
501}
502
503impl FromValue for FunctionStats {
504    fn from_value(value: Value) -> Result<Self> {
505        match &value {
506            Value::Array(Some(v)) if v.len() == 4 => {
507                fn into_result(values: &mut HashMap<String, Value>) -> Option<FunctionStats> {
508                    Some(FunctionStats {
509                        running_script: values.remove("running_script")?.into().ok()?,
510                        engines: values.remove("engines")?.into().ok()?,
511                    })
512                }
513
514                into_result(&mut value.into()?)
515                    .ok_or_else(|| Error::Client("Cannot parse FunctionStats".to_owned()))
516            }
517            _ => Err(Error::Client("Cannot parse FunctionStats".to_owned())),
518        }
519    }
520}
521
522#[derive(Debug)]
523pub struct RunningScript {
524    pub name: String,
525    pub command: Vec<String>,
526    pub duration_ms: u64,
527}
528
529impl FromValue for RunningScript {
530    fn from_value(value: Value) -> Result<Self> {
531        match &value {
532            Value::Array(Some(v)) if v.len() == 6 => {
533                fn into_result(values: &mut HashMap<String, Value>) -> Option<RunningScript> {
534                    Some(RunningScript {
535                        name: values.remove("name")?.into().ok()?,
536                        command: values.remove("command")?.into().ok()?,
537                        duration_ms: values.remove("duration_ms")?.into().ok()?,
538                    })
539                }
540
541                into_result(&mut value.into()?)
542                    .ok_or_else(|| Error::Client("Cannot parse RunningScript".to_owned()))
543            }
544            _ => Err(Error::Client("Cannot parse RunningScript".to_owned())),
545        }
546    }
547}
548
549#[derive(Debug, Default)]
550pub struct EngineStats {
551    pub libraries_count: usize,
552    pub functions_count: usize,
553}
554
555impl FromValue for EngineStats {
556    fn from_value(value: Value) -> Result<Self> {
557        match &value {
558            Value::Array(Some(v)) if v.len() == 4 => {
559                fn into_result(values: &mut HashMap<String, Value>) -> Option<EngineStats> {
560                    Some(EngineStats {
561                        libraries_count: values.remove("libraries_count")?.into().ok()?,
562                        functions_count: values.remove("functions_count")?.into().ok()?,
563                    })
564                }
565
566                into_result(&mut value.into()?)
567                    .ok_or_else(|| Error::Client("Cannot parse EngineStats".to_owned()))
568            }
569            _ => Err(Error::Client("Cannot parse EngineStats".to_owned())),
570        }
571    }
572}
573
574pub enum ScriptDebugMode {
575    Default,
576    Yes,
577    Sync,
578    No,
579}
580
581impl IntoArgs for ScriptDebugMode {
582    fn into_args(self, args: CommandArgs) -> CommandArgs {
583        match self {
584            ScriptDebugMode::Default => args,
585            ScriptDebugMode::Yes => args.arg("YES"),
586            ScriptDebugMode::Sync => args.arg("SYNC"),
587            ScriptDebugMode::No => args.arg("NO"),
588        }
589    }
590}
591
592/// Options for the [`function_list`](crate::ScriptingCommands::function_list) command
593#[derive(Default)]
594pub struct FunctionListOptions {
595    command_args: CommandArgs,
596}
597
598impl FunctionListOptions {
599    #[must_use]
600    pub fn library_name_pattern<P: Into<CommandArg>>(self, library_name_pattern: P) -> Self {
601        Self {
602            command_args: self
603                .command_args
604                .arg("LIBRARYNAME")
605                .arg(library_name_pattern),
606        }
607    }
608
609    #[must_use]
610    pub fn with_code(self) -> Self {
611        Self {
612            command_args: self.command_args.arg("WITHCODE"),
613        }
614    }
615}
616
617impl IntoArgs for FunctionListOptions {
618    fn into_args(self, args: CommandArgs) -> CommandArgs {
619        args.arg(self.command_args)
620    }
621}