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}