redis_driver/commands/
string_commands.rs

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