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}