1use crate::write::ToSingleValkeyArg;
8use crate::write::ToValkeyArgs;
9use crate::write::ValkeyWrite;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum ExpireOption {
16 NONE,
18 NX,
20 XX,
22 GT,
24 LT,
26}
27
28impl ToValkeyArgs for ExpireOption {
29 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
30 match self {
31 ExpireOption::NONE => {}
32 ExpireOption::NX => out.write_arg(b"NX"),
33 ExpireOption::XX => out.write_arg(b"XX"),
34 ExpireOption::GT => out.write_arg(b"GT"),
35 ExpireOption::LT => out.write_arg(b"LT"),
36 }
37 }
38}
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq)]
44pub enum SetExpiry {
45 EX(u64),
47 PX(u64),
49 EXAT(u64),
51 PXAT(u64),
53 KEEPTTL,
55}
56
57impl ToValkeyArgs for SetExpiry {
58 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
59 let mut kw = |k: &[u8], v: u64| {
60 out.write_arg(k);
61 out.write_arg_fmt(v);
62 };
63 match self {
64 SetExpiry::EX(secs) => kw(b"EX", *secs),
65 SetExpiry::PX(millis) => kw(b"PX", *millis),
66 SetExpiry::EXAT(ts) => kw(b"EXAT", *ts),
67 SetExpiry::PXAT(ts) => kw(b"PXAT", *ts),
68 SetExpiry::KEEPTTL => out.write_arg(b"KEEPTTL"),
69 }
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum ExistenceCheck {
78 NX,
80 XX,
82}
83
84impl ToValkeyArgs for ExistenceCheck {
85 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
86 out.write_arg(match self {
87 ExistenceCheck::NX => b"NX".as_slice(),
88 ExistenceCheck::XX => b"XX".as_slice(),
89 });
90 }
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
97pub enum FieldExistenceCheck {
98 FNX,
100 FXX,
102}
103
104impl ToValkeyArgs for FieldExistenceCheck {
105 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
106 out.write_arg(match self {
107 FieldExistenceCheck::FNX => b"FNX".as_slice(),
108 FieldExistenceCheck::FXX => b"FXX".as_slice(),
109 });
110 }
111}
112
113#[derive(Debug, Clone, Copy, Default)]
117pub struct HashFieldExpirationOptions {
118 existence_check: Option<FieldExistenceCheck>,
119 expiration: Option<SetExpiry>,
120}
121
122impl HashFieldExpirationOptions {
123 pub fn set_existence_check(mut self, field_existence_check: FieldExistenceCheck) -> Self {
125 self.existence_check = Some(field_existence_check);
126 self
127 }
128
129 pub fn set_expiration(mut self, expiration: SetExpiry) -> Self {
131 self.expiration = Some(expiration);
132 self
133 }
134}
135
136impl ToValkeyArgs for HashFieldExpirationOptions {
137 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
138 if let Some(ref existence_check) = self.existence_check {
139 existence_check.write_valkey_args(out);
140 }
141 if let Some(ref expiration) = self.expiration {
142 expiration.write_valkey_args(out);
143 }
144 }
145}
146
147#[derive(Debug, Clone, Copy)]
151pub struct CopyOptions<Db: ToString> {
152 db: Option<Db>,
153 replace: bool,
154}
155
156impl Default for CopyOptions<&'static str> {
157 fn default() -> Self {
158 CopyOptions {
159 db: None,
160 replace: false,
161 }
162 }
163}
164
165impl<Db: ToString> CopyOptions<Db> {
166 pub fn db<Db2: ToString>(self, db: Db2) -> CopyOptions<Db2> {
168 CopyOptions {
169 db: Some(db),
170 replace: self.replace,
171 }
172 }
173
174 pub fn replace(mut self, replace: bool) -> Self {
176 self.replace = replace;
177 self
178 }
179}
180
181impl<Db: ToString> ToValkeyArgs for CopyOptions<Db> {
182 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
183 if let Some(ref db) = self.db {
184 out.write_arg(b"DB");
185 out.write_arg(db.to_string().as_bytes());
186 }
187 if self.replace {
188 out.write_arg(b"REPLACE");
189 }
190 }
191}
192
193#[derive(Debug, Clone, Copy, Default)]
197pub struct FlushAllOptions {
198 pub blocking: bool,
200}
201
202impl FlushAllOptions {
203 pub fn blocking(mut self, blocking: bool) -> Self {
205 self.blocking = blocking;
206 self
207 }
208}
209
210impl ToValkeyArgs for FlushAllOptions {
211 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
212 out.write_arg(if self.blocking { b"SYNC" } else { b"ASYNC" });
213 }
214}
215
216pub type FlushDbOptions = FlushAllOptions;
220
221pub type FunctionFlushOptions = FlushAllOptions;
223
224#[derive(Debug, Clone, PartialEq, Eq)]
238#[non_exhaustive]
239pub enum ValueComparison {
240 IFEQ(Vec<u8>),
242}
243
244impl ValueComparison {
245 pub fn ifeq(value: impl ToSingleValkeyArg) -> Self {
247 Self::IFEQ(Self::arg_to_bytes(value))
248 }
249
250 fn arg_to_bytes(value: impl ToSingleValkeyArg) -> Vec<u8> {
251 value.to_valkey_args().swap_remove(0)
252 }
253}
254
255impl ToValkeyArgs for ValueComparison {
256 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
257 match self {
258 ValueComparison::IFEQ(value) => {
259 out.write_arg(b"IFEQ");
260 out.write_arg(value);
261 }
262 }
263 }
264}
265
266#[derive(Clone, Default)]
270pub struct SetOptions {
271 conditional_set: Option<ExistenceCheck>,
272 value_comparison: Option<ValueComparison>,
273 get: bool,
274 expiration: Option<SetExpiry>,
275}
276
277impl SetOptions {
278 pub fn conditional_set(mut self, existence_check: ExistenceCheck) -> Self {
280 self.conditional_set = Some(existence_check);
281 self
282 }
283
284 pub fn value_comparison(mut self, value_comparison: ValueComparison) -> Self {
287 self.value_comparison = Some(value_comparison);
288 self
289 }
290
291 pub fn get(mut self, get: bool) -> Self {
293 self.get = get;
294 self
295 }
296
297 pub fn with_expiration(mut self, expiration: SetExpiry) -> Self {
299 self.expiration = Some(expiration);
300 self
301 }
302}
303
304impl ToValkeyArgs for SetOptions {
305 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
306 if let Some(ref existence_check) = self.conditional_set {
307 existence_check.write_valkey_args(out);
308 }
309 if let Some(ref value_comparison) = self.value_comparison {
310 value_comparison.write_valkey_args(out);
311 }
312 if self.get {
313 out.write_arg(b"GET");
314 }
315 if let Some(ref expiration) = self.expiration {
316 expiration.write_valkey_args(out);
317 }
318 }
319}
320
321#[derive(Debug, Clone, Copy, PartialEq, Eq)]
325pub enum Direction {
326 Left,
328 Right,
330}
331
332impl ToValkeyArgs for Direction {
333 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
334 out.write_arg(match self {
335 Direction::Left => b"LEFT".as_slice(),
336 Direction::Right => b"RIGHT".as_slice(),
337 });
338 }
339}
340
341#[derive(Debug, Clone, Copy, PartialEq, Eq)]
345pub enum Expiry {
346 EX(u64),
348 PX(u64),
350 EXAT(u64),
352 PXAT(u64),
354 PERSIST,
356}
357
358impl ToValkeyArgs for Expiry {
359 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
360 let mut kw = |k: &[u8], v: u64| {
361 out.write_arg(k);
362 out.write_arg_fmt(v);
363 };
364 match self {
365 Expiry::EX(sec) => kw(b"EX", *sec),
366 Expiry::PX(ms) => kw(b"PX", *ms),
367 Expiry::EXAT(ts) => kw(b"EXAT", *ts),
368 Expiry::PXAT(ts) => kw(b"PXAT", *ts),
369 Expiry::PERSIST => out.write_arg(b"PERSIST"),
370 }
371 }
372}
373
374#[derive(Debug, Clone, Copy, Default)]
378pub struct LposOptions {
379 count: Option<usize>,
380 maxlen: Option<usize>,
381 rank: Option<isize>,
382}
383
384impl LposOptions {
385 pub fn count(mut self, n: usize) -> Self {
387 self.count = Some(n);
388 self
389 }
390
391 pub fn rank(mut self, n: isize) -> Self {
393 self.rank = Some(n);
394 self
395 }
396
397 pub fn maxlen(mut self, n: usize) -> Self {
399 self.maxlen = Some(n);
400 self
401 }
402}
403
404impl ToValkeyArgs for LposOptions {
405 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
406 if let Some(n) = self.count {
407 out.write_arg(b"COUNT");
408 out.write_arg_fmt(n);
409 }
410 if let Some(n) = self.rank {
411 out.write_arg(b"RANK");
412 out.write_arg_fmt(n);
413 }
414 if let Some(n) = self.maxlen {
415 out.write_arg(b"MAXLEN");
416 out.write_arg_fmt(n);
417 }
418 }
419}
420
421#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
425pub enum FunctionRestorePolicy {
426 #[default]
428 Append,
429 Flush,
431 Replace,
433}
434
435impl FunctionRestorePolicy {
436 pub(crate) fn as_arg(&self) -> &'static str {
437 match self {
438 FunctionRestorePolicy::Append => "APPEND",
439 FunctionRestorePolicy::Flush => "FLUSH",
440 FunctionRestorePolicy::Replace => "REPLACE",
441 }
442 }
443}
444
445#[derive(Debug, Clone, Copy, PartialEq, Eq)]
449pub enum ClientPauseMode {
450 All,
452 Write,
454}
455
456impl ClientPauseMode {
457 pub(crate) fn as_arg(&self) -> &'static str {
458 match self {
459 ClientPauseMode::All => "ALL",
460 ClientPauseMode::Write => "WRITE",
461 }
462 }
463}
464
465#[derive(Clone, Default)]
469pub struct MigrateOptions {
470 pub copy: bool,
472 pub replace: bool,
474 pub password: Option<String>,
476 pub username: Option<String>,
478}
479
480impl std::fmt::Debug for MigrateOptions {
481 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
482 f.debug_struct("MigrateOptions")
483 .field("copy", &self.copy)
484 .field("replace", &self.replace)
485 .field("password", &self.password.as_ref().map(|_| "<redacted>"))
486 .field("username", &self.username)
487 .finish()
488 }
489}
490
491impl ToValkeyArgs for MigrateOptions {
492 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
493 if self.copy {
494 out.write_arg(b"COPY");
495 }
496 if self.replace {
497 out.write_arg(b"REPLACE");
498 }
499 match (&self.username, &self.password) {
500 (Some(username), Some(password)) => {
501 out.write_arg(b"AUTH2");
502 out.write_arg(username.as_bytes());
503 out.write_arg(password.as_bytes());
504 }
505 (None, Some(password)) => {
506 out.write_arg(b"AUTH");
507 out.write_arg(password.as_bytes());
508 }
509 _ => {}
510 }
511 }
512}
513
514#[derive(Debug, Clone, Copy, Default)]
518pub struct RestoreOptions {
519 pub replace: bool,
521 pub absttl: bool,
523 pub idletime: Option<i64>,
525 pub frequency: Option<i64>,
527}
528
529impl ToValkeyArgs for RestoreOptions {
530 fn write_valkey_args<W: ?Sized + ValkeyWrite>(&self, out: &mut W) {
531 if self.replace {
532 out.write_arg(b"REPLACE");
533 }
534 if self.absttl {
535 out.write_arg(b"ABSTTL");
536 }
537 if let Some(idletime) = self.idletime {
538 out.write_arg(b"IDLETIME");
539 out.write_arg_fmt(idletime);
540 }
541 if let Some(frequency) = self.frequency {
542 out.write_arg(b"FREQ");
543 out.write_arg_fmt(frequency);
544 }
545 }
546}
547
548#[derive(Debug, Clone, Copy, PartialEq, Eq)]
552pub enum OrderBy {
553 Asc,
555 Desc,
557}
558
559impl OrderBy {
560 pub(crate) fn as_arg(&self) -> &'static str {
561 match self {
562 OrderBy::Asc => "ASC",
563 OrderBy::Desc => "DESC",
564 }
565 }
566}
567
568#[derive(Debug, Clone, Copy, PartialEq, Eq)]
572pub struct Limit {
573 pub offset: i64,
575 pub count: i64,
577}
578
579#[derive(Debug, Clone, Copy, PartialEq, Eq)]
581#[non_exhaustive]
582pub enum ObjectType {
583 String,
585 List,
587 Set,
589 ZSet,
591 Hash,
593 Stream,
595}
596
597impl ObjectType {
598 pub(crate) fn to_redis(self) -> redis::ObjectType {
600 match self {
601 ObjectType::String => redis::ObjectType::String,
602 ObjectType::List => redis::ObjectType::List,
603 ObjectType::Set => redis::ObjectType::Set,
604 ObjectType::ZSet => redis::ObjectType::ZSet,
605 ObjectType::Hash => redis::ObjectType::Hash,
606 ObjectType::Stream => redis::ObjectType::Stream,
607 }
608 }
609}
610
611#[cfg(test)]
612mod tests {
613 use super::*;
614 use crate::test_utils::assert_args;
615 use crate::test_utils::assert_args_empty;
616
617 #[test]
618 fn existence_check_args() {
619 assert_args(ExistenceCheck::NX, &["NX"]);
620 assert_args(ExistenceCheck::XX, &["XX"]);
621 }
622
623 #[test]
624 fn set_expiry_args() {
625 assert_args(SetExpiry::EX(60), &["EX", "60"]);
626 assert_args(SetExpiry::PX(1500), &["PX", "1500"]);
627 assert_args(SetExpiry::EXAT(100), &["EXAT", "100"]);
628 assert_args(SetExpiry::PXAT(200), &["PXAT", "200"]);
629 assert_args(SetExpiry::KEEPTTL, &["KEEPTTL"]);
630 assert_args(SetExpiry::PXAT(u64::MAX), &["PXAT", "18446744073709551615"]);
631 }
632
633 #[test]
634 fn direction_args() {
635 assert_args(Direction::Left, &["LEFT"]);
636 assert_args(Direction::Right, &["RIGHT"]);
637 }
638
639 #[test]
640 fn expiry_args() {
641 assert_args(Expiry::EX(60), &["EX", "60"]);
642 assert_args(Expiry::PX(1500), &["PX", "1500"]);
643 assert_args(Expiry::EXAT(100), &["EXAT", "100"]);
644 assert_args(Expiry::PXAT(200), &["PXAT", "200"]);
645 assert_args(Expiry::PERSIST, &["PERSIST"]);
646 assert_args(Expiry::EXAT(u64::MAX), &["EXAT", "18446744073709551615"]);
647 }
648
649 #[test]
650 fn lpos_options_args() {
651 assert_args_empty(LposOptions::default());
652 assert_args(
653 LposOptions::default().count(2).rank(-1).maxlen(100),
654 &["COUNT", "2", "RANK", "-1", "MAXLEN", "100"],
655 );
656 }
657
658 #[test]
659 fn set_options_args() {
660 assert_args_empty(SetOptions::default());
661 assert_args(
662 SetOptions::default()
663 .conditional_set(ExistenceCheck::NX)
664 .get(true)
665 .with_expiration(SetExpiry::EX(60)),
666 &["NX", "GET", "EX", "60"],
667 );
668 assert_args(
669 SetOptions::default()
670 .conditional_set(ExistenceCheck::XX)
671 .value_comparison(ValueComparison::ifeq("old"))
672 .get(true)
673 .with_expiration(SetExpiry::PX(1500)),
674 &["XX", "IFEQ", "old", "GET", "PX", "1500"],
675 );
676 }
677
678 #[test]
679 fn value_comparison_args() {
680 assert_args(ValueComparison::ifeq("v"), &["IFEQ", "v"]);
681 assert_args(
682 ValueComparison::IFEQ([0u8].to_vec()),
683 &[b"IFEQ".as_slice(), &[0u8]],
684 );
685
686 }
688
689 #[test]
690 fn expire_option_args() {
691 assert_args_empty(ExpireOption::NONE);
692 assert_args(ExpireOption::NX, &["NX"]);
693 assert_args(ExpireOption::XX, &["XX"]);
694 assert_args(ExpireOption::GT, &["GT"]);
695 assert_args(ExpireOption::LT, &["LT"]);
696 }
697
698 #[test]
699 fn hash_field_expiration_options_args() {
700 assert_args_empty(HashFieldExpirationOptions::default());
701 assert_args(
702 HashFieldExpirationOptions::default()
703 .set_existence_check(FieldExistenceCheck::FNX)
704 .set_expiration(SetExpiry::PX(1500)),
705 &["FNX", "PX", "1500"],
706 );
707 assert_args(
708 HashFieldExpirationOptions::default().set_existence_check(FieldExistenceCheck::FXX),
709 &["FXX"],
710 );
711 }
712
713 #[test]
714 fn copy_options_args() {
715 assert_args_empty(CopyOptions::default());
716 assert_args(CopyOptions::default().replace(true), &["REPLACE"]);
717 assert_args(
718 CopyOptions::default().db(2).replace(true),
719 &["DB", "2", "REPLACE"],
720 );
721 }
722
723 #[test]
724 fn flush_all_options_args() {
725 assert_args(FlushAllOptions::default(), &["ASYNC"]);
726 assert_args(FlushDbOptions::default().blocking(true), &["SYNC"]);
727 assert_args(FunctionFlushOptions::default().blocking(true), &["SYNC"]);
728 }
729
730 #[test]
731 fn order_by_args() {
732 assert_eq!(OrderBy::Asc.as_arg(), "ASC");
733 assert_eq!(OrderBy::Desc.as_arg(), "DESC");
734 }
735
736 #[test]
737 fn restore_options_args() {
738 assert_args_empty(RestoreOptions::default());
739 assert_args(
740 RestoreOptions {
741 replace: true,
742 absttl: true,
743 idletime: Some(100),
744 frequency: Some(5),
745 },
746 &["REPLACE", "ABSTTL", "IDLETIME", "100", "FREQ", "5"],
747 );
748 }
749
750 #[test]
751 fn migrate_options_args() {
752 assert_args_empty(MigrateOptions::default());
753 assert_args(
754 MigrateOptions {
755 copy: true,
756 replace: true,
757 password: Some("pw".into()),
758 username: None,
759 },
760 &["COPY", "REPLACE", "AUTH", "pw"],
761 );
762 assert_args(
763 MigrateOptions {
764 copy: false,
765 replace: false,
766 password: Some("pw".into()),
767 username: Some("user".into()),
768 },
769 &["AUTH2", "user", "pw"],
770 );
771 }
772
773 #[test]
774 fn migrate_options_debug_redacts_password() {
775 let opts = MigrateOptions {
776 copy: true,
777 replace: false,
778 password: Some("super-secret".into()),
779 username: Some("alice".into()),
780 };
781
782 let redacted_str = format!("{opts:?}");
783 assert!(!redacted_str.contains("super-secret"));
784 assert!(redacted_str.contains("<redacted>"));
785
786 let unredacted_str = format!("{:?}", MigrateOptions::default());
787 assert!(!unredacted_str.contains("<redacted>"));
788 }
789
790 #[test]
791 fn client_pause_mode_args() {
792 assert_eq!(ClientPauseMode::All.as_arg(), "ALL");
793 assert_eq!(ClientPauseMode::Write.as_arg(), "WRITE");
794 }
795
796 #[test]
797 fn function_restore_policy_args() {
798 assert_eq!(FunctionRestorePolicy::Append.as_arg(), "APPEND");
799 assert_eq!(FunctionRestorePolicy::Flush.as_arg(), "FLUSH");
800 assert_eq!(FunctionRestorePolicy::Replace.as_arg(), "REPLACE");
801 }
802}