rustis/commands/
string_commands.rs

1use crate::{
2    client::{prepare_command, PreparedCommand},
3    resp::{
4        cmd, CollectionResponse, CommandArgs, KeyValueArgsCollection, PrimitiveResponse, SingleArg,
5        SingleArgCollection, ToArgs,
6    },
7};
8use serde::{
9    de::{self, SeqAccess, Visitor},
10    Deserialize, Deserializer,
11};
12use std::fmt;
13
14/// A group of Redis commands related to [`Strings`](https://redis.io/docs/data-types/strings/)
15/// # See Also
16/// [Redis Generic Commands](https://redis.io/commands/?group=string)
17pub trait StringCommands<'a> {
18    /// If key already exists and is a string,
19    /// this command appends the value at the end of the string.
20    /// If key does not exist it is created and set as an empty string,
21    /// so APPEND will be similar to SET in this special case.
22    ///
23    /// # Return
24    /// the length of the string after the append operation.
25    ///
26    /// # See Also
27    /// [<https://redis.io/commands/append/>](https://redis.io/commands/append/)
28    #[must_use]
29    fn append<K, V>(self, key: K, value: V) -> PreparedCommand<'a, Self, usize>
30    where
31        Self: Sized,
32        K: SingleArg,
33        V: SingleArg,
34    {
35        prepare_command(self, cmd("APPEND").arg(key).arg(value))
36    }
37
38    /// Decrements the number stored at key by one.
39    ///
40    /// If the key does not exist, it is set to 0 before performing the operation.
41    /// An error is returned if the key contains a value of the wrong type or contains
42    /// a string that can not be represented as integer.
43    /// This operation is limited to 64 bit signed integers.
44    ///
45    /// # Return
46    /// the value of key after the decrement
47    ///
48    /// # See Also
49    /// [<https://redis.io/commands/decr/>](https://redis.io/commands/decr/)
50    #[must_use]
51    fn decr<K>(self, key: K) -> PreparedCommand<'a, Self, i64>
52    where
53        Self: Sized,
54        K: SingleArg,
55    {
56        prepare_command(self, cmd("DECR").arg(key))
57    }
58
59    /// Decrements the number stored at key by one.
60    ///
61    /// If the key does not exist, it is set to 0 before performing the operation.
62    /// An error is returned if the key contains a value of the wrong type or contains
63    /// a string that can not be represented as integer.
64    /// This operation is limited to 64 bit signed integers.
65    ///
66    /// # Return
67    /// the value of key after the decrement
68    ///
69    /// # See Also
70    /// [<https://redis.io/commands/decrby/>](https://redis.io/commands/decrby/)
71    #[must_use]
72    fn decrby<K>(self, key: K, decrement: i64) -> PreparedCommand<'a, Self, i64>
73    where
74        Self: Sized,
75        K: SingleArg,
76    {
77        prepare_command(self, cmd("DECRBY").arg(key).arg(decrement))
78    }
79
80    /// Get the value of key.
81    ///
82    /// Get the value of key. If the key does not exist the special
83    /// value nil is returned. An error is returned if the value
84    /// stored at key is not a string, because GET only handles
85    /// string values.
86    ///
87    /// # Return
88    /// the value of key, or `nil` when key does not exist.
89    ///
90    /// # Example
91    /// ```
92    /// use rustis::{
93    ///     client::{Client, ClientPreparedCommand},
94    ///     commands::{FlushingMode, ServerCommands, StringCommands},
95    ///     resp::{cmd},
96    ///     Result
97    /// };
98    ///
99    /// #[cfg_attr(feature = "tokio-runtime", tokio::main)]
100    /// #[cfg_attr(feature = "async-std-runtime", async_std::main)]
101    /// async fn main() -> Result<()> {
102    ///     let client = Client::connect("127.0.0.1:6379").await?;
103    ///     client.flushdb(FlushingMode::Sync).await?;
104    ///
105    ///     // return value can be an Option<String>...
106    ///     let value: Option<String> = client.get("key").await?;
107    ///     assert_eq!(None, value);
108    ///
109    ///     // ... or it can be directly a String.
110    ///     // In this cas a `nil` value will result in an empty String
111    ///     let value: String = client.get("key").await?;
112    ///     assert_eq!("", value);
113    ///
114    ///     client.set("key", "value").await?;
115    ///     let value: String = client.get("key").await?;
116    ///     assert_eq!("value", value);
117    ///
118    ///     Ok(())
119    /// }
120    /// ```
121    ///
122    /// # See Also
123    /// [<https://redis.io/commands/get/>](https://redis.io/commands/get/)
124    #[must_use]
125    fn get<K, V>(self, key: K) -> PreparedCommand<'a, Self, V>
126    where
127        Self: Sized,
128        K: SingleArg,
129        V: PrimitiveResponse,
130    {
131        prepare_command(self, cmd("GET").arg(key))
132    }
133
134    /// Get the value of key and delete the key.
135    ///
136    /// This command is similar to GET, except for the fact that it also deletes the key on success
137    /// (if and only if the key's value type is a string).
138    ///
139    /// # Return
140    /// the value of key, `nil` when key does not exist, or an error if the key's value type isn't a string.
141    ///
142    /// # See Also
143    /// [<https://redis.io/commands/getdel/>](https://redis.io/commands/getdel/)
144    #[must_use]
145    fn getdel<K, V>(self, key: K) -> PreparedCommand<'a, Self, V>
146    where
147        Self: Sized,
148        K: SingleArg,
149        V: PrimitiveResponse,
150    {
151        prepare_command(self, cmd("GETDEL").arg(key))
152    }
153
154    /// Get the value of key and optionally set its expiration. GETEX is similar to GET, but is a write command with additional options.
155    ///
156    /// Decrements the number stored at key by decrement.
157    /// If the key does not exist, it is set to 0 before performing the operation.
158    /// An error is returned if the key contains a value of the wrong type
159    /// or contains a string that can not be represented as integer.
160    /// This operation is limited to 64 bit signed integers.
161    ///
162    /// # Return
163    /// the value of key, or `nil` when key does not exist.
164    ///
165    /// # Example
166    /// ```
167    /// use rustis::{
168    ///     client::{Client, ClientPreparedCommand},
169    ///     commands::{FlushingMode, GetExOptions, GenericCommands, ServerCommands, StringCommands},
170    ///     resp::cmd,
171    ///     Result,
172    /// };
173    ///
174    /// #[cfg_attr(feature = "tokio-runtime", tokio::main)]
175    /// #[cfg_attr(feature = "async-std-runtime", async_std::main)]
176    /// async fn main() -> Result<()> {
177    ///     let client = Client::connect("127.0.0.1:6379").await?;
178    ///     client.flushdb(FlushingMode::Sync).await?;
179    ///
180    ///     client.set("key", "value").await?;
181    ///     let value: String = client.getex("key", GetExOptions::Ex(60)).await?;
182    ///     assert_eq!("value", value);
183    ///
184    ///     let ttl = client.ttl("key").await?;
185    ///     assert!(59 <= ttl && ttl <= 60);
186    ///
187    ///     Ok(())
188    /// }
189    /// ```
190    ///
191    /// # See Also
192    /// [<https://redis.io/commands/getex/>](https://redis.io/commands/getex/)
193    #[must_use]
194    fn getex<K, V>(self, key: K, options: GetExOptions) -> PreparedCommand<'a, Self, V>
195    where
196        Self: Sized,
197        K: SingleArg,
198        V: PrimitiveResponse,
199    {
200        prepare_command(self, cmd("GETEX").arg(key).arg(options))
201    }
202
203    /// Returns the substring of the string value stored at key, determined by the offsets start and end (both are inclusive).
204    ///
205    /// Negative offsets can be used in order to provide an offset starting from the end of the string.
206    /// So -1 means the last character, -2 the penultimate and so forth.
207    ///
208    /// The function handles out of range requests by limiting the resulting range to the actual length of the string.
209    ///
210    /// # Example
211    /// ```
212    /// # use rustis::{
213    /// #    client::Client,
214    /// #    commands::{FlushingMode, ServerCommands, StringCommands},
215    /// #    Result,
216    /// # };
217    ///
218    /// # #[cfg_attr(feature = "tokio-runtime", tokio::main)]
219    /// # #[cfg_attr(feature = "async-std-runtime", async_std::main)]
220    /// # async fn main() -> Result<()> {
221    /// #    let client = Client::connect("127.0.0.1:6379").await?;
222    /// #    client.flushdb(FlushingMode::Sync).await?;
223    /// client.set("mykey", "This is a string").await?;
224    ///
225    /// let value: String = client.getrange("mykey", 0, 3).await?;
226    /// assert_eq!("This", value);
227    /// let value: String = client.getrange("mykey", -3, -1).await?;
228    /// assert_eq!("ing", value);
229    /// let value: String = client.getrange("mykey", 0, -1).await?;
230    /// assert_eq!("This is a string", value);
231    /// let value: String = client.getrange("mykey", 10, 100).await?;
232    /// assert_eq!("string", value);
233    /// #    Ok(())
234    /// # }
235    /// ```
236    ///
237    /// # See Also
238    /// [<https://redis.io/commands/getrange/>](https://redis.io/commands/getrange/)
239    #[must_use]
240    fn getrange<K, V>(self, key: K, start: isize, end: isize) -> PreparedCommand<'a, Self, V>
241    where
242        Self: Sized,
243        K: SingleArg,
244        V: PrimitiveResponse,
245    {
246        prepare_command(self, cmd("GETRANGE").arg(key).arg(start).arg(end))
247    }
248
249    /// Atomically sets key to value and returns the old value stored at key.
250    /// Returns an error when key exists but does not hold a string value.
251    /// Any previous time to live associated with the key is discarded on successful SET operation.
252    ///
253    /// # Return
254    /// the old value stored at key, or nil when key did not exist.
255    ///
256    /// # See Also
257    /// [<https://redis.io/commands/getset/>](https://redis.io/commands/getset/)
258    #[must_use]
259    fn getset<K, V, R>(self, key: K, value: V) -> PreparedCommand<'a, Self, R>
260    where
261        Self: Sized,
262        K: SingleArg,
263        V: SingleArg,
264        R: PrimitiveResponse,
265    {
266        prepare_command(self, cmd("GETSET").arg(key).arg(value))
267    }
268
269    /// Increments the number stored at key by one.
270    ///
271    /// If the key does not exist, it is set to 0 before performing the operation.
272    /// An error is returned if the key contains a value of the wrong type
273    /// or contains a string that can not be represented as integer.
274    /// This operation is limited to 64 bit signed integers.
275    ///
276    /// Note: this is a string operation because Redis does not have a dedicated integer type.
277    /// The string stored at the key is interpreted as a base-10 64 bit signed integer to execute the operation.
278    ///
279    /// Redis stores integers in their integer representation, so for string values that actually hold an integer,
280    /// there is no overhead for storing the string representation of the integer.
281    ///
282    /// # Return
283    /// the value of key after the increment
284    ///
285    /// # See Also
286    /// [<https://redis.io/commands/incr/>](https://redis.io/commands/incr/)
287    #[must_use]
288    fn incr<K>(self, key: K) -> PreparedCommand<'a, Self, i64>
289    where
290        Self: Sized,
291        K: SingleArg,
292    {
293        prepare_command(self, cmd("INCR").arg(key))
294    }
295
296    /// Increments the number stored at key by increment.
297    ///
298    /// If the key does not exist, it is set to 0 before performing the operation.
299    /// An error is returned if the key contains a value of the wrong type
300    /// or contains a string that can not be represented as integer.
301    /// This operation is limited to 64 bit signed integers.
302    ///
303    /// See [incr](StringCommands::incr) for extra information on increment/decrement operations.
304    ///
305    /// # Return
306    /// the value of key after the increment
307    ///
308    /// # See Also
309    /// [<https://redis.io/commands/incrby/>](https://redis.io/commands/incrby/)
310    #[must_use]
311    fn incrby<K>(self, key: K, increment: i64) -> PreparedCommand<'a, Self, i64>
312    where
313        Self: Sized,
314        K: SingleArg,
315    {
316        prepare_command(self, cmd("INCRBY").arg(key).arg(increment))
317    }
318
319    ///Increment the string representing a floating point number stored at key by the specified increment.
320    /// By using a negative increment value, the result is that the value stored at the key is decremented (by the obvious properties of addition).
321    /// If the key does not exist, it is set to 0 before performing the operation.
322    /// An error is returned if one of the following conditions occur:
323    ///
324    /// - The key contains a value of the wrong type (not a string).
325    ///
326    /// - The current key content or the specified increment are not parsable as a double precision floating point number.
327    ///
328    /// If the command is successful the new incremented value is stored as the new value of the key (replacing the old one),
329    /// and returned to the caller as a string.
330    ///   
331    /// Both the value already contained in the string key and the increment argument can be optionally provided in exponential notation,
332    /// however the value computed after the increment is stored consistently in the same format, that is,
333    /// an integer number followed (if needed) by a dot, and a variable number of digits representing the decimal part of the number.
334    /// Trailing zeroes are always removed.
335    ///    
336    /// The precision of the output is fixed at 17 digits after the decimal point
337    /// regardless of the actual internal precision of the computation.
338    ///
339    /// # Return
340    /// the value of key after the increment
341    ///
342    /// # See Also
343    /// [<https://redis.io/commands/incrbyfloat/>](https://redis.io/commands/incrbyfloat/)
344    #[must_use]
345    fn incrbyfloat<K>(self, key: K, increment: f64) -> PreparedCommand<'a, Self, f64>
346    where
347        Self: Sized,
348        K: SingleArg,
349    {
350        prepare_command(self, cmd("INCRBYFLOAT").arg(key).arg(increment))
351    }
352
353    /// The LCS command implements the longest common subsequence algorithm
354    ///
355    /// # Return
356    /// The string representing the longest common substring.
357    ///
358    /// # See Also
359    /// [<https://redis.io/commands/lcs/>](https://redis.io/commands/lcs/)
360    #[must_use]
361    fn lcs<K, V>(self, key1: K, key2: K) -> PreparedCommand<'a, Self, V>
362    where
363        Self: Sized,
364        K: SingleArg,
365        V: PrimitiveResponse,
366    {
367        prepare_command(self, cmd("LCS").arg(key1).arg(key2))
368    }
369
370    /// The LCS command implements the longest common subsequence algorithm
371    ///
372    /// # Return
373    /// The length of the longest common substring.
374    ///
375    /// # See Also
376    /// [<https://redis.io/commands/lcs/>](https://redis.io/commands/lcs/)
377    #[must_use]
378    fn lcs_len<K>(self, key1: K, key2: K) -> PreparedCommand<'a, Self, usize>
379    where
380        Self: Sized,
381        K: SingleArg,
382    {
383        prepare_command(self, cmd("LCS").arg(key1).arg(key2).arg("LEN"))
384    }
385
386    /// The LCS command implements the longest common subsequence algorithm
387    ///
388    /// # Return
389    /// An array with the LCS length and all the ranges in both the strings,
390    /// start and end offset for each string, where there are matches.
391    /// When `with_match_len` is given each match will also have the length of the match
392    ///
393    /// # See Also
394    /// [<https://redis.io/commands/lcs/>](https://redis.io/commands/lcs/)
395    #[must_use]
396    fn lcs_idx<K>(
397        self,
398        key1: K,
399        key2: K,
400        min_match_len: Option<usize>,
401        with_match_len: bool,
402    ) -> PreparedCommand<'a, Self, LcsResult>
403    where
404        Self: Sized,
405        K: SingleArg,
406    {
407        prepare_command(
408            self,
409            cmd("LCS")
410                .arg(key1)
411                .arg(key2)
412                .arg("IDX")
413                .arg(min_match_len.map(|len| ("MINMATCHLEN", len)))
414                .arg_if(with_match_len, "WITHMATCHLEN"),
415        )
416    }
417
418    /// Returns the values of all specified keys.
419    ///
420    /// For every key that does not hold a string value or does not exist,
421    /// the special value nil is returned. Because of this, the operation never fails.
422    ///
423    /// # Return
424    /// Array reply: list of values at the specified keys.
425    ///
426    /// # See Also
427    /// [<https://redis.io/commands/mget/>](https://redis.io/commands/mget/)
428    #[must_use]
429    fn mget<K, KK, V, VV>(self, keys: KK) -> PreparedCommand<'a, Self, VV>
430    where
431        Self: Sized,
432        K: SingleArg,
433        KK: SingleArgCollection<K>,
434        V: PrimitiveResponse + serde::de::DeserializeOwned,
435        VV: CollectionResponse<V>,
436    {
437        prepare_command(self, cmd("MGET").arg(keys))
438    }
439
440    /// Sets the given keys to their respective values.
441    ///
442    /// # Return
443    /// always OK since MSET can't fail.
444    ///
445    /// # See Also
446    /// [<https://redis.io/commands/mset/>](https://redis.io/commands/mset/)
447    #[must_use]
448    fn mset<K, V, C>(self, items: C) -> PreparedCommand<'a, Self, ()>
449    where
450        Self: Sized,
451        C: KeyValueArgsCollection<K, V>,
452        K: SingleArg,
453        V: SingleArg,
454    {
455        prepare_command(self, cmd("MSET").arg(items))
456    }
457
458    /// Sets the given keys to their respective values.
459    /// MSETNX will not perform any operation at all even if just a single key already exists.
460    ///
461    /// Because of this semantic MSETNX can be used in order to set different keys representing
462    /// different fields of a unique logic object in a way that ensures that either
463    /// all the fields or none at all are set.
464    ///
465    /// MSETNX is atomic, so all given keys are set at once. It is not possible for
466    /// clients to see that some of the keys were updated while others are unchanged.
467    ///
468    /// # Return
469    /// specifically:
470    /// - 1 if the all the keys were set.
471    /// - 0 if no key was set (at least one key already existed).
472    ///
473    /// # See Also
474    /// [<https://redis.io/commands/msetnx/>](https://redis.io/commands/msetnx/)
475    #[must_use]
476    fn msetnx<K, V, C>(self, items: C) -> PreparedCommand<'a, Self, bool>
477    where
478        Self: Sized,
479        C: KeyValueArgsCollection<K, V>,
480        K: SingleArg,
481        V: SingleArg,
482    {
483        prepare_command(self, cmd("MSETNX").arg(items))
484    }
485
486    /// Works exactly like [setex](StringCommands::setex) with the sole
487    /// difference that the expire time is specified in milliseconds instead of seconds.
488    ///
489    /// If key already holds a value, it is overwritten, regardless of its type.
490    /// Any previous time to live associated with the key is discarded on successful SET operation.
491    ///
492    /// # See Also
493    /// [<https://redis.io/commands/psetex/>](https://redis.io/commands/psetex/)
494    #[must_use]
495    fn psetex<K, V>(self, key: K, milliseconds: u64, value: V) -> PreparedCommand<'a, Self, ()>
496    where
497        Self: Sized,
498        K: SingleArg,
499        V: SingleArg,
500    {
501        prepare_command(self, cmd("PSETEX").arg(key).arg(milliseconds).arg(value))
502    }
503
504    ///Set key to hold the string value.
505    ///
506    /// If key already holds a value, it is overwritten, regardless of its type.
507    /// Any previous time to live associated with the key is discarded on successful SET operation.
508    ///
509    /// # See Also
510    /// [<https://redis.io/commands/set/>](https://redis.io/commands/set/)
511    #[must_use]
512    fn set<K, V>(self, key: K, value: V) -> PreparedCommand<'a, Self, ()>
513    where
514        Self: Sized,
515        K: SingleArg,
516        V: SingleArg,
517        Self: Sized,
518    {
519        prepare_command(self, cmd("SET").arg(key).arg(value))
520    }
521
522    /// Set key to hold the string value.
523    ///
524    /// # Return
525    /// * `true` if SET was executed correctly.
526    /// * `false` if the SET operation was not performed because the user
527    ///   specified the NX or XX option but the condition was not met.
528    ///
529    /// # See Also
530    /// [<https://redis.io/commands/set/>](https://redis.io/commands/set/)
531    #[must_use]
532    fn set_with_options<K, V>(
533        self,
534        key: K,
535        value: V,
536        condition: SetCondition,
537        expiration: SetExpiration,
538        keep_ttl: bool,
539    ) -> PreparedCommand<'a, Self, bool>
540    where
541        Self: Sized,
542        K: SingleArg,
543        V: SingleArg,
544    {
545        prepare_command(
546            self,
547            cmd("SET")
548                .arg(key)
549                .arg(value)
550                .arg(condition)
551                .arg(expiration)
552                .arg_if(keep_ttl, "KEEPTTL"),
553        )
554    }
555
556    /// Set key to hold the string value wit GET option enforced
557    ///
558    /// # See Also
559    /// [<https://redis.io/commands/set/>](https://redis.io/commands/set/)
560    #[must_use]
561    fn set_get_with_options<K, V1, V2>(
562        self,
563        key: K,
564        value: V1,
565        condition: SetCondition,
566        expiration: SetExpiration,
567        keep_ttl: bool,
568    ) -> PreparedCommand<'a, Self, V2>
569    where
570        Self: Sized,
571        K: SingleArg,
572        V1: SingleArg,
573        V2: PrimitiveResponse,
574    {
575        prepare_command(
576            self,
577            cmd("SET")
578                .arg(key)
579                .arg(value)
580                .arg(condition)
581                .arg("GET")
582                .arg(expiration)
583                .arg_if(keep_ttl, "KEEPTTL"),
584        )
585    }
586
587    /// Set key to hold the string value and set key to timeout after a given number of seconds.
588    ///
589    /// # See Also
590    /// [<https://redis.io/commands/setex/>](https://redis.io/commands/setex/)
591    #[must_use]
592    fn setex<K, V>(self, key: K, seconds: u64, value: V) -> PreparedCommand<'a, Self, ()>
593    where
594        Self: Sized,
595        K: SingleArg,
596        V: SingleArg,
597    {
598        prepare_command(self, cmd("SETEX").arg(key).arg(seconds).arg(value))
599    }
600
601    /// Set key to hold string value if key does not exist.
602    ///
603    /// In that case, it is equal to SET.
604    /// When key already holds a value, no operation is performed.
605    /// SETNX is short for "SET if Not eXists".
606    ///
607    /// # Return
608    /// specifically:
609    /// * `true` - if the key was set
610    /// * `false` - if the key was not set
611    ///
612    /// # See Also
613    /// [<https://redis.io/commands/setnx/>](https://redis.io/commands/setnx/)
614    #[must_use]
615    fn setnx<K, V>(self, key: K, value: V) -> PreparedCommand<'a, Self, bool>
616    where
617        Self: Sized,
618        K: SingleArg,
619        V: SingleArg,
620    {
621        prepare_command(self, cmd("SETNX").arg(key).arg(value))
622    }
623
624    /// Overwrites part of the string stored at key,
625    /// starting at the specified offset,
626    /// for the entire length of value.
627    ///
628    /// # Return
629    /// the length of the string after it was modified by the command.
630    ///
631    /// # See Also
632    /// [<https://redis.io/commands/setrange/>](https://redis.io/commands/setrange/)
633    #[must_use]
634    fn setrange<K, V>(self, key: K, offset: usize, value: V) -> PreparedCommand<'a, Self, usize>
635    where
636        Self: Sized,
637        K: SingleArg,
638        V: SingleArg,
639    {
640        prepare_command(self, cmd("SETRANGE").arg(key).arg(offset).arg(value))
641    }
642
643    /// Returns the length of the string value stored at key.
644    ///
645    /// An error is returned when key holds a non-string value.
646    ///
647    /// # Return
648    /// the length of the string at key, or 0 when key does not exist.
649    ///
650    /// # See Also
651    /// [<https://redis.io/commands/strlen/>](https://redis.io/commands/strlen/)
652    #[must_use]
653    fn strlen<K>(self, key: K) -> PreparedCommand<'a, Self, usize>
654    where
655        Self: Sized,
656        K: SingleArg,
657    {
658        prepare_command(self, cmd("STRLEN").arg(key))
659    }
660
661    /// Returns the substring of the string value stored at key, determined by the offsets start and end (both are inclusive).
662    ///
663    /// Negative offsets can be used in order to provide an offset starting from the end of the string.
664    /// So -1 means the last character, -2 the penultimate and so forth.
665    ///
666    /// The function handles out of range requests by limiting the resulting range to the actual length of the string.
667    ///
668    /// # Example
669    /// ```
670    /// # use rustis::{
671    /// #    client::Client,
672    /// #    commands::{FlushingMode, ServerCommands, StringCommands},
673    /// #    Result,
674    /// # };
675    ///
676    /// # #[cfg_attr(feature = "tokio-runtime", tokio::main)]
677    /// # #[cfg_attr(feature = "async-std-runtime", async_std::main)]
678    /// # async fn main() -> Result<()> {
679    /// #    let client = Client::connect("127.0.0.1:6379").await?;
680    /// #    client.flushdb(FlushingMode::Sync).await?;
681    /// client.set("mykey", "This is a string").await?;
682    ///
683    /// let value: String = client.substr("mykey", 0, 3).await?;
684    /// assert_eq!("This", value);
685    /// let value: String = client.substr("mykey", -3, -1).await?;
686    /// assert_eq!("ing", value);
687    /// let value: String = client.substr("mykey", 0, -1).await?;
688    /// assert_eq!("This is a string", value);
689    /// let value: String = client.substr("mykey", 10, 100).await?;
690    /// assert_eq!("string", value);
691    /// #    Ok(())
692    /// # }
693    /// ```
694    ///
695    /// # See Also
696    /// [<https://redis.io/commands/substr/>](https://redis.io/commands/substr/)
697    #[must_use]
698    fn substr<K, V>(self, key: K, start: isize, end: isize) -> PreparedCommand<'a, Self, V>
699    where
700        Self: Sized,
701        K: SingleArg,
702        V: PrimitiveResponse,
703    {
704        prepare_command(self, cmd("SUBSTR").arg(key).arg(start).arg(end))
705    }
706}
707
708/// Options for the [`getex`](StringCommands::getex) and the [`hgetex`](crate::commands::HashCommands::hgetex) commands
709pub enum GetExOptions {
710    /// Set the specified expire time, in seconds.
711    Ex(u64),
712    /// Set the specified expire time, in milliseconds.
713    Px(u64),
714    /// Set the specified Unix time at which the key will expire, in seconds.
715    Exat(u64),
716    /// Set the specified Unix time at which the key will expire, in milliseconds.
717    Pxat(u64),
718    /// Remove the time to live associated with the key.
719    Persist,
720}
721
722impl ToArgs for GetExOptions {
723    fn write_args(&self, args: &mut CommandArgs) {
724        match self {
725            GetExOptions::Ex(duration) => args.arg(("EX", *duration)),
726            GetExOptions::Px(duration) => args.arg(("PX", *duration)),
727            GetExOptions::Exat(timestamp) => args.arg(("EXAT", *timestamp)),
728            GetExOptions::Pxat(timestamp) => args.arg(("PXAT", *timestamp)),
729            GetExOptions::Persist => args.arg("PERSIST"),
730        };
731    }
732}
733
734/// Part of the result for the [`lcs`](StringCommands::lcs) command
735#[derive(Debug, PartialEq, Eq)]
736pub struct LcsMatch(pub (usize, usize), pub (usize, usize), pub Option<usize>);
737
738impl<'de> Deserialize<'de> for LcsMatch {
739    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
740    where
741        D: Deserializer<'de>,
742    {
743        struct LcsMatchVisitor;
744
745        impl<'de> Visitor<'de> for LcsMatchVisitor {
746            type Value = LcsMatch;
747
748            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
749                formatter.write_str("LcsMatch")
750            }
751
752            fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Self::Value, A::Error>
753            where
754                A: SeqAccess<'de>,
755            {
756                let Some(first): Option<(usize, usize)> = seq.next_element()? else {
757                    return Err(de::Error::invalid_length(0, &"fewer elements in tuple"));
758                };
759
760                let Some(second): Option<(usize, usize)> = seq.next_element()? else {
761                    return Err(de::Error::invalid_length(1, &"fewer elements in tuple"));
762                };
763
764                let match_len: Option<usize> = seq.next_element()?;
765
766                Ok(LcsMatch(first, second, match_len))
767            }
768        }
769
770        deserializer.deserialize_seq(LcsMatchVisitor)
771    }
772}
773
774/// Result for the [`lcs`](StringCommands::lcs) command
775#[derive(Debug, Deserialize)]
776pub struct LcsResult {
777    pub matches: Vec<LcsMatch>,
778    pub len: usize,
779}
780
781/// Expiration option for the [`set_with_options`](StringCommands::set_with_options) and [`hsetex`](crate::commands::HashCommands::hsetex) commands
782#[derive(Default)]
783pub enum SetExpiration {
784    /// No expiration
785    #[default]
786    None,
787    /// Set the specified expire time, in seconds.
788    Ex(u64),
789    /// Set the specified expire time, in milliseconds.
790    Px(u64),
791    /// Set the specified Unix time at which the key will expire, in seconds.
792    Exat(u64),
793    /// Set the specified Unix time at which the key will expire, in milliseconds.
794    Pxat(u64),
795}
796
797impl ToArgs for SetExpiration {
798    fn write_args(&self, args: &mut CommandArgs) {
799        match self {
800            SetExpiration::None => {}
801            SetExpiration::Ex(duration) => {
802                args.arg(("EX", *duration));
803            }
804            SetExpiration::Px(duration) => {
805                args.arg(("PX", *duration));
806            }
807            SetExpiration::Exat(timestamp) => {
808                args.arg(("EXAT", *timestamp));
809            }
810            SetExpiration::Pxat(timestamp) => {
811                args.arg(("PXAT", *timestamp));
812            }
813        };
814    }
815}
816
817/// Condition option for the [`set_with_options`](StringCommands::set_with_options) command
818#[derive(Default)]
819pub enum SetCondition {
820    /// No condition
821    #[default]
822    None,
823    /// Only set the key if it does not already exist.
824    NX,
825    /// Only set the key if it already exist.
826    XX,
827}
828
829impl ToArgs for SetCondition {
830    fn write_args(&self, args: &mut CommandArgs) {
831        match self {
832            SetCondition::None => {}
833            SetCondition::NX => {
834                args.arg("NX");
835            }
836            SetCondition::XX => {
837                args.arg("XX");
838            }
839        }
840    }
841}