rustis/commands/
string_commands.rs

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