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