1use bytes::Bytes;
8
9use crate::error::ProtocolError;
10use crate::types::Frame;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum SetExpire {
15 Ex(u64),
17 Px(u64),
19}
20
21#[derive(Debug, Clone, PartialEq)]
23pub enum Command {
24 Ping(Option<Bytes>),
26
27 Echo(Bytes),
29
30 Get { key: String },
32
33 Set {
35 key: String,
36 value: Bytes,
37 expire: Option<SetExpire>,
38 nx: bool,
40 xx: bool,
42 },
43
44 Incr { key: String },
46
47 Decr { key: String },
49
50 Del { keys: Vec<String> },
52
53 Exists { keys: Vec<String> },
55
56 MGet { keys: Vec<String> },
58
59 MSet { pairs: Vec<(String, Bytes)> },
61
62 Expire { key: String, seconds: u64 },
64
65 Ttl { key: String },
67
68 Persist { key: String },
70
71 Pttl { key: String },
73
74 Pexpire { key: String, milliseconds: u64 },
76
77 DbSize,
79
80 Info { section: Option<String> },
82
83 BgSave,
85
86 BgRewriteAof,
88
89 FlushDb,
91
92 Scan {
94 cursor: u64,
95 pattern: Option<String>,
96 count: Option<usize>,
97 },
98
99 LPush { key: String, values: Vec<Bytes> },
101
102 RPush { key: String, values: Vec<Bytes> },
104
105 LPop { key: String },
107
108 RPop { key: String },
110
111 LRange { key: String, start: i64, stop: i64 },
113
114 LLen { key: String },
116
117 Type { key: String },
119
120 ZAdd {
122 key: String,
123 flags: ZAddFlags,
124 members: Vec<(f64, String)>,
125 },
126
127 ZRem { key: String, members: Vec<String> },
129
130 ZScore { key: String, member: String },
132
133 ZRank { key: String, member: String },
135
136 ZCard { key: String },
138
139 ZRange {
141 key: String,
142 start: i64,
143 stop: i64,
144 with_scores: bool,
145 },
146
147 HSet {
149 key: String,
150 fields: Vec<(String, Bytes)>,
151 },
152
153 HGet { key: String, field: String },
155
156 HGetAll { key: String },
158
159 HDel { key: String, fields: Vec<String> },
161
162 HExists { key: String, field: String },
164
165 HLen { key: String },
167
168 HIncrBy {
170 key: String,
171 field: String,
172 delta: i64,
173 },
174
175 HKeys { key: String },
177
178 HVals { key: String },
180
181 HMGet { key: String, fields: Vec<String> },
183
184 SAdd { key: String, members: Vec<String> },
186
187 SRem { key: String, members: Vec<String> },
189
190 SMembers { key: String },
192
193 SIsMember { key: String, member: String },
195
196 SCard { key: String },
198
199 ClusterInfo,
202
203 ClusterNodes,
205
206 ClusterSlots,
208
209 ClusterKeySlot { key: String },
211
212 ClusterMyId,
214
215 ClusterSetSlotImporting { slot: u16, node_id: String },
217
218 ClusterSetSlotMigrating { slot: u16, node_id: String },
220
221 ClusterSetSlotNode { slot: u16, node_id: String },
223
224 ClusterSetSlotStable { slot: u16 },
226
227 ClusterMeet { ip: String, port: u16 },
229
230 ClusterAddSlots { slots: Vec<u16> },
232
233 ClusterDelSlots { slots: Vec<u16> },
235
236 ClusterForget { node_id: String },
238
239 ClusterReplicate { node_id: String },
241
242 ClusterFailover { force: bool, takeover: bool },
244
245 ClusterCountKeysInSlot { slot: u16 },
247
248 ClusterGetKeysInSlot { slot: u16, count: u32 },
250
251 Migrate {
254 host: String,
255 port: u16,
256 key: String,
257 db: u32,
258 timeout_ms: u64,
259 copy: bool,
260 replace: bool,
261 },
262
263 Asking,
265
266 SlowLogGet { count: Option<usize> },
268
269 SlowLogLen,
271
272 SlowLogReset,
274
275 Unknown(String),
277}
278
279#[derive(Debug, Clone, Default, PartialEq)]
281pub struct ZAddFlags {
282 pub nx: bool,
284 pub xx: bool,
286 pub gt: bool,
288 pub lt: bool,
290 pub ch: bool,
292}
293
294impl Eq for ZAddFlags {}
295
296impl Command {
297 pub fn command_name(&self) -> &'static str {
302 match self {
303 Command::Ping(_) => "ping",
304 Command::Echo(_) => "echo",
305 Command::Get { .. } => "get",
306 Command::Set { .. } => "set",
307 Command::Incr { .. } => "incr",
308 Command::Decr { .. } => "decr",
309 Command::Del { .. } => "del",
310 Command::Exists { .. } => "exists",
311 Command::MGet { .. } => "mget",
312 Command::MSet { .. } => "mset",
313 Command::Expire { .. } => "expire",
314 Command::Ttl { .. } => "ttl",
315 Command::Persist { .. } => "persist",
316 Command::Pttl { .. } => "pttl",
317 Command::Pexpire { .. } => "pexpire",
318 Command::DbSize => "dbsize",
319 Command::Info { .. } => "info",
320 Command::BgSave => "bgsave",
321 Command::BgRewriteAof => "bgrewriteaof",
322 Command::FlushDb => "flushdb",
323 Command::Scan { .. } => "scan",
324 Command::LPush { .. } => "lpush",
325 Command::RPush { .. } => "rpush",
326 Command::LPop { .. } => "lpop",
327 Command::RPop { .. } => "rpop",
328 Command::LRange { .. } => "lrange",
329 Command::LLen { .. } => "llen",
330 Command::Type { .. } => "type",
331 Command::ZAdd { .. } => "zadd",
332 Command::ZRem { .. } => "zrem",
333 Command::ZScore { .. } => "zscore",
334 Command::ZRank { .. } => "zrank",
335 Command::ZCard { .. } => "zcard",
336 Command::ZRange { .. } => "zrange",
337 Command::HSet { .. } => "hset",
338 Command::HGet { .. } => "hget",
339 Command::HGetAll { .. } => "hgetall",
340 Command::HDel { .. } => "hdel",
341 Command::HExists { .. } => "hexists",
342 Command::HLen { .. } => "hlen",
343 Command::HIncrBy { .. } => "hincrby",
344 Command::HKeys { .. } => "hkeys",
345 Command::HVals { .. } => "hvals",
346 Command::HMGet { .. } => "hmget",
347 Command::SAdd { .. } => "sadd",
348 Command::SRem { .. } => "srem",
349 Command::SMembers { .. } => "smembers",
350 Command::SIsMember { .. } => "sismember",
351 Command::SCard { .. } => "scard",
352 Command::ClusterInfo => "cluster_info",
353 Command::ClusterNodes => "cluster_nodes",
354 Command::ClusterSlots => "cluster_slots",
355 Command::ClusterKeySlot { .. } => "cluster_keyslot",
356 Command::ClusterMyId => "cluster_myid",
357 Command::ClusterSetSlotImporting { .. } => "cluster_setslot",
358 Command::ClusterSetSlotMigrating { .. } => "cluster_setslot",
359 Command::ClusterSetSlotNode { .. } => "cluster_setslot",
360 Command::ClusterSetSlotStable { .. } => "cluster_setslot",
361 Command::ClusterMeet { .. } => "cluster_meet",
362 Command::ClusterAddSlots { .. } => "cluster_addslots",
363 Command::ClusterDelSlots { .. } => "cluster_delslots",
364 Command::ClusterForget { .. } => "cluster_forget",
365 Command::ClusterReplicate { .. } => "cluster_replicate",
366 Command::ClusterFailover { .. } => "cluster_failover",
367 Command::ClusterCountKeysInSlot { .. } => "cluster_countkeysinslot",
368 Command::ClusterGetKeysInSlot { .. } => "cluster_getkeysinslot",
369 Command::Migrate { .. } => "migrate",
370 Command::Asking => "asking",
371 Command::SlowLogGet { .. } => "slowlog",
372 Command::SlowLogLen => "slowlog",
373 Command::SlowLogReset => "slowlog",
374 Command::Unknown(_) => "unknown",
375 }
376 }
377
378 pub fn from_frame(frame: Frame) -> Result<Command, ProtocolError> {
383 let frames = match frame {
384 Frame::Array(frames) => frames,
385 _ => {
386 return Err(ProtocolError::InvalidCommandFrame(
387 "expected array frame".into(),
388 ));
389 }
390 };
391
392 if frames.is_empty() {
393 return Err(ProtocolError::InvalidCommandFrame(
394 "empty command array".into(),
395 ));
396 }
397
398 let name = extract_string(&frames[0])?;
399 let name_upper = name.to_ascii_uppercase();
400
401 match name_upper.as_str() {
402 "PING" => parse_ping(&frames[1..]),
403 "ECHO" => parse_echo(&frames[1..]),
404 "GET" => parse_get(&frames[1..]),
405 "SET" => parse_set(&frames[1..]),
406 "INCR" => parse_incr(&frames[1..]),
407 "DECR" => parse_decr(&frames[1..]),
408 "DEL" => parse_del(&frames[1..]),
409 "EXISTS" => parse_exists(&frames[1..]),
410 "MGET" => parse_mget(&frames[1..]),
411 "MSET" => parse_mset(&frames[1..]),
412 "EXPIRE" => parse_expire(&frames[1..]),
413 "TTL" => parse_ttl(&frames[1..]),
414 "PERSIST" => parse_persist(&frames[1..]),
415 "PTTL" => parse_pttl(&frames[1..]),
416 "PEXPIRE" => parse_pexpire(&frames[1..]),
417 "DBSIZE" => parse_dbsize(&frames[1..]),
418 "INFO" => parse_info(&frames[1..]),
419 "BGSAVE" => parse_bgsave(&frames[1..]),
420 "BGREWRITEAOF" => parse_bgrewriteaof(&frames[1..]),
421 "FLUSHDB" => parse_flushdb(&frames[1..]),
422 "SCAN" => parse_scan(&frames[1..]),
423 "LPUSH" => parse_lpush(&frames[1..]),
424 "RPUSH" => parse_rpush(&frames[1..]),
425 "LPOP" => parse_lpop(&frames[1..]),
426 "RPOP" => parse_rpop(&frames[1..]),
427 "LRANGE" => parse_lrange(&frames[1..]),
428 "LLEN" => parse_llen(&frames[1..]),
429 "TYPE" => parse_type(&frames[1..]),
430 "ZADD" => parse_zadd(&frames[1..]),
431 "ZREM" => parse_zrem(&frames[1..]),
432 "ZSCORE" => parse_zscore(&frames[1..]),
433 "ZRANK" => parse_zrank(&frames[1..]),
434 "ZCARD" => parse_zcard(&frames[1..]),
435 "ZRANGE" => parse_zrange(&frames[1..]),
436 "HSET" => parse_hset(&frames[1..]),
437 "HGET" => parse_hget(&frames[1..]),
438 "HGETALL" => parse_hgetall(&frames[1..]),
439 "HDEL" => parse_hdel(&frames[1..]),
440 "HEXISTS" => parse_hexists(&frames[1..]),
441 "HLEN" => parse_hlen(&frames[1..]),
442 "HINCRBY" => parse_hincrby(&frames[1..]),
443 "HKEYS" => parse_hkeys(&frames[1..]),
444 "HVALS" => parse_hvals(&frames[1..]),
445 "HMGET" => parse_hmget(&frames[1..]),
446 "SADD" => parse_sadd(&frames[1..]),
447 "SREM" => parse_srem(&frames[1..]),
448 "SMEMBERS" => parse_smembers(&frames[1..]),
449 "SISMEMBER" => parse_sismember(&frames[1..]),
450 "SCARD" => parse_scard(&frames[1..]),
451 "CLUSTER" => parse_cluster(&frames[1..]),
452 "ASKING" => parse_asking(&frames[1..]),
453 "MIGRATE" => parse_migrate(&frames[1..]),
454 "SLOWLOG" => parse_slowlog(&frames[1..]),
455 _ => Ok(Command::Unknown(name)),
456 }
457 }
458}
459
460fn extract_string(frame: &Frame) -> Result<String, ProtocolError> {
462 match frame {
463 Frame::Bulk(data) => String::from_utf8(data.to_vec()).map_err(|_| {
464 ProtocolError::InvalidCommandFrame("command name is not valid utf-8".into())
465 }),
466 Frame::Simple(s) => Ok(s.clone()),
467 _ => Err(ProtocolError::InvalidCommandFrame(
468 "expected bulk or simple string for command name".into(),
469 )),
470 }
471}
472
473fn extract_bytes(frame: &Frame) -> Result<Bytes, ProtocolError> {
475 match frame {
476 Frame::Bulk(data) => Ok(data.clone()),
477 Frame::Simple(s) => Ok(Bytes::from(s.clone().into_bytes())),
478 _ => Err(ProtocolError::InvalidCommandFrame(
479 "expected bulk or simple string argument".into(),
480 )),
481 }
482}
483
484fn parse_u64(frame: &Frame, cmd: &str) -> Result<u64, ProtocolError> {
486 let s = extract_string(frame)?;
487 s.parse::<u64>().map_err(|_| {
488 ProtocolError::InvalidCommandFrame(format!("value is not a valid integer for '{cmd}'"))
489 })
490}
491
492fn parse_ping(args: &[Frame]) -> Result<Command, ProtocolError> {
493 match args.len() {
494 0 => Ok(Command::Ping(None)),
495 1 => {
496 let msg = extract_bytes(&args[0])?;
497 Ok(Command::Ping(Some(msg)))
498 }
499 _ => Err(ProtocolError::WrongArity("PING".into())),
500 }
501}
502
503fn parse_echo(args: &[Frame]) -> Result<Command, ProtocolError> {
504 if args.len() != 1 {
505 return Err(ProtocolError::WrongArity("ECHO".into()));
506 }
507 let msg = extract_bytes(&args[0])?;
508 Ok(Command::Echo(msg))
509}
510
511fn parse_get(args: &[Frame]) -> Result<Command, ProtocolError> {
512 if args.len() != 1 {
513 return Err(ProtocolError::WrongArity("GET".into()));
514 }
515 let key = extract_string(&args[0])?;
516 Ok(Command::Get { key })
517}
518
519fn parse_set(args: &[Frame]) -> Result<Command, ProtocolError> {
520 if args.len() < 2 {
521 return Err(ProtocolError::WrongArity("SET".into()));
522 }
523
524 let key = extract_string(&args[0])?;
525 let value = extract_bytes(&args[1])?;
526
527 let mut expire = None;
528 let mut nx = false;
529 let mut xx = false;
530 let mut idx = 2;
531
532 while idx < args.len() {
533 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
534 match flag.as_str() {
535 "NX" => {
536 nx = true;
537 idx += 1;
538 }
539 "XX" => {
540 xx = true;
541 idx += 1;
542 }
543 "EX" => {
544 idx += 1;
545 if idx >= args.len() {
546 return Err(ProtocolError::WrongArity("SET".into()));
547 }
548 let amount = parse_u64(&args[idx], "SET")?;
549 if amount == 0 {
550 return Err(ProtocolError::InvalidCommandFrame(
551 "invalid expire time in 'SET' command".into(),
552 ));
553 }
554 expire = Some(SetExpire::Ex(amount));
555 idx += 1;
556 }
557 "PX" => {
558 idx += 1;
559 if idx >= args.len() {
560 return Err(ProtocolError::WrongArity("SET".into()));
561 }
562 let amount = parse_u64(&args[idx], "SET")?;
563 if amount == 0 {
564 return Err(ProtocolError::InvalidCommandFrame(
565 "invalid expire time in 'SET' command".into(),
566 ));
567 }
568 expire = Some(SetExpire::Px(amount));
569 idx += 1;
570 }
571 _ => {
572 return Err(ProtocolError::InvalidCommandFrame(format!(
573 "unsupported SET option '{flag}'"
574 )));
575 }
576 }
577 }
578
579 if nx && xx {
580 return Err(ProtocolError::InvalidCommandFrame(
581 "XX and NX options at the same time are not compatible".into(),
582 ));
583 }
584
585 Ok(Command::Set {
586 key,
587 value,
588 expire,
589 nx,
590 xx,
591 })
592}
593
594fn parse_incr(args: &[Frame]) -> Result<Command, ProtocolError> {
595 if args.len() != 1 {
596 return Err(ProtocolError::WrongArity("INCR".into()));
597 }
598 let key = extract_string(&args[0])?;
599 Ok(Command::Incr { key })
600}
601
602fn parse_decr(args: &[Frame]) -> Result<Command, ProtocolError> {
603 if args.len() != 1 {
604 return Err(ProtocolError::WrongArity("DECR".into()));
605 }
606 let key = extract_string(&args[0])?;
607 Ok(Command::Decr { key })
608}
609
610fn parse_del(args: &[Frame]) -> Result<Command, ProtocolError> {
611 if args.is_empty() {
612 return Err(ProtocolError::WrongArity("DEL".into()));
613 }
614 let keys = args
615 .iter()
616 .map(extract_string)
617 .collect::<Result<Vec<_>, _>>()?;
618 Ok(Command::Del { keys })
619}
620
621fn parse_exists(args: &[Frame]) -> Result<Command, ProtocolError> {
622 if args.is_empty() {
623 return Err(ProtocolError::WrongArity("EXISTS".into()));
624 }
625 let keys = args
626 .iter()
627 .map(extract_string)
628 .collect::<Result<Vec<_>, _>>()?;
629 Ok(Command::Exists { keys })
630}
631
632fn parse_mget(args: &[Frame]) -> Result<Command, ProtocolError> {
633 if args.is_empty() {
634 return Err(ProtocolError::WrongArity("MGET".into()));
635 }
636 let keys = args
637 .iter()
638 .map(extract_string)
639 .collect::<Result<Vec<_>, _>>()?;
640 Ok(Command::MGet { keys })
641}
642
643fn parse_mset(args: &[Frame]) -> Result<Command, ProtocolError> {
644 if args.is_empty() || !args.len().is_multiple_of(2) {
645 return Err(ProtocolError::WrongArity("MSET".into()));
646 }
647 let mut pairs = Vec::with_capacity(args.len() / 2);
648 for chunk in args.chunks(2) {
649 let key = extract_string(&chunk[0])?;
650 let value = extract_bytes(&chunk[1])?;
651 pairs.push((key, value));
652 }
653 Ok(Command::MSet { pairs })
654}
655
656fn parse_expire(args: &[Frame]) -> Result<Command, ProtocolError> {
657 if args.len() != 2 {
658 return Err(ProtocolError::WrongArity("EXPIRE".into()));
659 }
660 let key = extract_string(&args[0])?;
661 let seconds = parse_u64(&args[1], "EXPIRE")?;
662
663 if seconds == 0 {
664 return Err(ProtocolError::InvalidCommandFrame(
665 "invalid expire time in 'EXPIRE' command".into(),
666 ));
667 }
668
669 Ok(Command::Expire { key, seconds })
670}
671
672fn parse_ttl(args: &[Frame]) -> Result<Command, ProtocolError> {
673 if args.len() != 1 {
674 return Err(ProtocolError::WrongArity("TTL".into()));
675 }
676 let key = extract_string(&args[0])?;
677 Ok(Command::Ttl { key })
678}
679
680fn parse_persist(args: &[Frame]) -> Result<Command, ProtocolError> {
681 if args.len() != 1 {
682 return Err(ProtocolError::WrongArity("PERSIST".into()));
683 }
684 let key = extract_string(&args[0])?;
685 Ok(Command::Persist { key })
686}
687
688fn parse_pttl(args: &[Frame]) -> Result<Command, ProtocolError> {
689 if args.len() != 1 {
690 return Err(ProtocolError::WrongArity("PTTL".into()));
691 }
692 let key = extract_string(&args[0])?;
693 Ok(Command::Pttl { key })
694}
695
696fn parse_pexpire(args: &[Frame]) -> Result<Command, ProtocolError> {
697 if args.len() != 2 {
698 return Err(ProtocolError::WrongArity("PEXPIRE".into()));
699 }
700 let key = extract_string(&args[0])?;
701 let milliseconds = parse_u64(&args[1], "PEXPIRE")?;
702
703 if milliseconds == 0 {
704 return Err(ProtocolError::InvalidCommandFrame(
705 "invalid expire time in 'PEXPIRE' command".into(),
706 ));
707 }
708
709 Ok(Command::Pexpire { key, milliseconds })
710}
711
712fn parse_dbsize(args: &[Frame]) -> Result<Command, ProtocolError> {
713 if !args.is_empty() {
714 return Err(ProtocolError::WrongArity("DBSIZE".into()));
715 }
716 Ok(Command::DbSize)
717}
718
719fn parse_info(args: &[Frame]) -> Result<Command, ProtocolError> {
720 match args.len() {
721 0 => Ok(Command::Info { section: None }),
722 1 => {
723 let section = extract_string(&args[0])?;
724 Ok(Command::Info {
725 section: Some(section),
726 })
727 }
728 _ => Err(ProtocolError::WrongArity("INFO".into())),
729 }
730}
731
732fn parse_bgsave(args: &[Frame]) -> Result<Command, ProtocolError> {
733 if !args.is_empty() {
734 return Err(ProtocolError::WrongArity("BGSAVE".into()));
735 }
736 Ok(Command::BgSave)
737}
738
739fn parse_bgrewriteaof(args: &[Frame]) -> Result<Command, ProtocolError> {
740 if !args.is_empty() {
741 return Err(ProtocolError::WrongArity("BGREWRITEAOF".into()));
742 }
743 Ok(Command::BgRewriteAof)
744}
745
746fn parse_flushdb(args: &[Frame]) -> Result<Command, ProtocolError> {
747 if !args.is_empty() {
748 return Err(ProtocolError::WrongArity("FLUSHDB".into()));
749 }
750 Ok(Command::FlushDb)
751}
752
753fn parse_scan(args: &[Frame]) -> Result<Command, ProtocolError> {
754 if args.is_empty() {
755 return Err(ProtocolError::WrongArity("SCAN".into()));
756 }
757
758 let cursor = parse_u64(&args[0], "SCAN")?;
759 let mut pattern = None;
760 let mut count = None;
761 let mut idx = 1;
762
763 while idx < args.len() {
764 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
765 match flag.as_str() {
766 "MATCH" => {
767 idx += 1;
768 if idx >= args.len() {
769 return Err(ProtocolError::WrongArity("SCAN".into()));
770 }
771 pattern = Some(extract_string(&args[idx])?);
772 idx += 1;
773 }
774 "COUNT" => {
775 idx += 1;
776 if idx >= args.len() {
777 return Err(ProtocolError::WrongArity("SCAN".into()));
778 }
779 let n = parse_u64(&args[idx], "SCAN")?;
780 count = Some(n as usize);
781 idx += 1;
782 }
783 _ => {
784 return Err(ProtocolError::InvalidCommandFrame(format!(
785 "unsupported SCAN option '{flag}'"
786 )));
787 }
788 }
789 }
790
791 Ok(Command::Scan {
792 cursor,
793 pattern,
794 count,
795 })
796}
797
798fn parse_i64(frame: &Frame, cmd: &str) -> Result<i64, ProtocolError> {
800 let s = extract_string(frame)?;
801 s.parse::<i64>().map_err(|_| {
802 ProtocolError::InvalidCommandFrame(format!("value is not a valid integer for '{cmd}'"))
803 })
804}
805
806fn parse_lpush(args: &[Frame]) -> Result<Command, ProtocolError> {
807 if args.len() < 2 {
808 return Err(ProtocolError::WrongArity("LPUSH".into()));
809 }
810 let key = extract_string(&args[0])?;
811 let values = args[1..]
812 .iter()
813 .map(extract_bytes)
814 .collect::<Result<Vec<_>, _>>()?;
815 Ok(Command::LPush { key, values })
816}
817
818fn parse_rpush(args: &[Frame]) -> Result<Command, ProtocolError> {
819 if args.len() < 2 {
820 return Err(ProtocolError::WrongArity("RPUSH".into()));
821 }
822 let key = extract_string(&args[0])?;
823 let values = args[1..]
824 .iter()
825 .map(extract_bytes)
826 .collect::<Result<Vec<_>, _>>()?;
827 Ok(Command::RPush { key, values })
828}
829
830fn parse_lpop(args: &[Frame]) -> Result<Command, ProtocolError> {
831 if args.len() != 1 {
832 return Err(ProtocolError::WrongArity("LPOP".into()));
833 }
834 let key = extract_string(&args[0])?;
835 Ok(Command::LPop { key })
836}
837
838fn parse_rpop(args: &[Frame]) -> Result<Command, ProtocolError> {
839 if args.len() != 1 {
840 return Err(ProtocolError::WrongArity("RPOP".into()));
841 }
842 let key = extract_string(&args[0])?;
843 Ok(Command::RPop { key })
844}
845
846fn parse_lrange(args: &[Frame]) -> Result<Command, ProtocolError> {
847 if args.len() != 3 {
848 return Err(ProtocolError::WrongArity("LRANGE".into()));
849 }
850 let key = extract_string(&args[0])?;
851 let start = parse_i64(&args[1], "LRANGE")?;
852 let stop = parse_i64(&args[2], "LRANGE")?;
853 Ok(Command::LRange { key, start, stop })
854}
855
856fn parse_llen(args: &[Frame]) -> Result<Command, ProtocolError> {
857 if args.len() != 1 {
858 return Err(ProtocolError::WrongArity("LLEN".into()));
859 }
860 let key = extract_string(&args[0])?;
861 Ok(Command::LLen { key })
862}
863
864fn parse_type(args: &[Frame]) -> Result<Command, ProtocolError> {
865 if args.len() != 1 {
866 return Err(ProtocolError::WrongArity("TYPE".into()));
867 }
868 let key = extract_string(&args[0])?;
869 Ok(Command::Type { key })
870}
871
872fn parse_f64(frame: &Frame, cmd: &str) -> Result<f64, ProtocolError> {
874 let s = extract_string(frame)?;
875 let v = s.parse::<f64>().map_err(|_| {
876 ProtocolError::InvalidCommandFrame(format!("value is not a valid float for '{cmd}'"))
877 })?;
878 if v.is_nan() {
879 return Err(ProtocolError::InvalidCommandFrame(format!(
880 "NaN is not a valid score for '{cmd}'"
881 )));
882 }
883 Ok(v)
884}
885
886fn parse_zadd(args: &[Frame]) -> Result<Command, ProtocolError> {
887 if args.len() < 3 {
889 return Err(ProtocolError::WrongArity("ZADD".into()));
890 }
891
892 let key = extract_string(&args[0])?;
893 let mut flags = ZAddFlags::default();
894 let mut idx = 1;
895
896 while idx < args.len() {
898 let s = extract_string(&args[idx])?.to_ascii_uppercase();
899 match s.as_str() {
900 "NX" => {
901 flags.nx = true;
902 idx += 1;
903 }
904 "XX" => {
905 flags.xx = true;
906 idx += 1;
907 }
908 "GT" => {
909 flags.gt = true;
910 idx += 1;
911 }
912 "LT" => {
913 flags.lt = true;
914 idx += 1;
915 }
916 "CH" => {
917 flags.ch = true;
918 idx += 1;
919 }
920 _ => break,
921 }
922 }
923
924 if flags.nx && flags.xx {
926 return Err(ProtocolError::InvalidCommandFrame(
927 "XX and NX options at the same time are not compatible".into(),
928 ));
929 }
930 if flags.gt && flags.lt {
932 return Err(ProtocolError::InvalidCommandFrame(
933 "GT and LT options at the same time are not compatible".into(),
934 ));
935 }
936
937 let remaining = &args[idx..];
939 if remaining.is_empty() || !remaining.len().is_multiple_of(2) {
940 return Err(ProtocolError::WrongArity("ZADD".into()));
941 }
942
943 let mut members = Vec::with_capacity(remaining.len() / 2);
944 for pair in remaining.chunks(2) {
945 let score = parse_f64(&pair[0], "ZADD")?;
946 let member = extract_string(&pair[1])?;
947 members.push((score, member));
948 }
949
950 Ok(Command::ZAdd {
951 key,
952 flags,
953 members,
954 })
955}
956
957fn parse_zcard(args: &[Frame]) -> Result<Command, ProtocolError> {
958 if args.len() != 1 {
959 return Err(ProtocolError::WrongArity("ZCARD".into()));
960 }
961 let key = extract_string(&args[0])?;
962 Ok(Command::ZCard { key })
963}
964
965fn parse_zrem(args: &[Frame]) -> Result<Command, ProtocolError> {
966 if args.len() < 2 {
967 return Err(ProtocolError::WrongArity("ZREM".into()));
968 }
969 let key = extract_string(&args[0])?;
970 let members = args[1..]
971 .iter()
972 .map(extract_string)
973 .collect::<Result<Vec<_>, _>>()?;
974 Ok(Command::ZRem { key, members })
975}
976
977fn parse_zscore(args: &[Frame]) -> Result<Command, ProtocolError> {
978 if args.len() != 2 {
979 return Err(ProtocolError::WrongArity("ZSCORE".into()));
980 }
981 let key = extract_string(&args[0])?;
982 let member = extract_string(&args[1])?;
983 Ok(Command::ZScore { key, member })
984}
985
986fn parse_zrank(args: &[Frame]) -> Result<Command, ProtocolError> {
987 if args.len() != 2 {
988 return Err(ProtocolError::WrongArity("ZRANK".into()));
989 }
990 let key = extract_string(&args[0])?;
991 let member = extract_string(&args[1])?;
992 Ok(Command::ZRank { key, member })
993}
994
995fn parse_zrange(args: &[Frame]) -> Result<Command, ProtocolError> {
996 if args.len() < 3 || args.len() > 4 {
997 return Err(ProtocolError::WrongArity("ZRANGE".into()));
998 }
999 let key = extract_string(&args[0])?;
1000 let start = parse_i64(&args[1], "ZRANGE")?;
1001 let stop = parse_i64(&args[2], "ZRANGE")?;
1002
1003 let with_scores = if args.len() == 4 {
1004 let opt = extract_string(&args[3])?.to_ascii_uppercase();
1005 if opt != "WITHSCORES" {
1006 return Err(ProtocolError::InvalidCommandFrame(format!(
1007 "unsupported ZRANGE option '{opt}'"
1008 )));
1009 }
1010 true
1011 } else {
1012 false
1013 };
1014
1015 Ok(Command::ZRange {
1016 key,
1017 start,
1018 stop,
1019 with_scores,
1020 })
1021}
1022
1023fn parse_hset(args: &[Frame]) -> Result<Command, ProtocolError> {
1026 if args.len() < 3 || !(args.len() - 1).is_multiple_of(2) {
1030 return Err(ProtocolError::WrongArity("HSET".into()));
1031 }
1032
1033 let key = extract_string(&args[0])?;
1034 let mut fields = Vec::with_capacity((args.len() - 1) / 2);
1035
1036 for chunk in args[1..].chunks(2) {
1037 let field = extract_string(&chunk[0])?;
1038 let value = extract_bytes(&chunk[1])?;
1039 fields.push((field, value));
1040 }
1041
1042 Ok(Command::HSet { key, fields })
1043}
1044
1045fn parse_hget(args: &[Frame]) -> Result<Command, ProtocolError> {
1046 if args.len() != 2 {
1047 return Err(ProtocolError::WrongArity("HGET".into()));
1048 }
1049 let key = extract_string(&args[0])?;
1050 let field = extract_string(&args[1])?;
1051 Ok(Command::HGet { key, field })
1052}
1053
1054fn parse_hgetall(args: &[Frame]) -> Result<Command, ProtocolError> {
1055 if args.len() != 1 {
1056 return Err(ProtocolError::WrongArity("HGETALL".into()));
1057 }
1058 let key = extract_string(&args[0])?;
1059 Ok(Command::HGetAll { key })
1060}
1061
1062fn parse_hdel(args: &[Frame]) -> Result<Command, ProtocolError> {
1063 if args.len() < 2 {
1064 return Err(ProtocolError::WrongArity("HDEL".into()));
1065 }
1066 let key = extract_string(&args[0])?;
1067 let fields = args[1..]
1068 .iter()
1069 .map(extract_string)
1070 .collect::<Result<Vec<_>, _>>()?;
1071 Ok(Command::HDel { key, fields })
1072}
1073
1074fn parse_hexists(args: &[Frame]) -> Result<Command, ProtocolError> {
1075 if args.len() != 2 {
1076 return Err(ProtocolError::WrongArity("HEXISTS".into()));
1077 }
1078 let key = extract_string(&args[0])?;
1079 let field = extract_string(&args[1])?;
1080 Ok(Command::HExists { key, field })
1081}
1082
1083fn parse_hlen(args: &[Frame]) -> Result<Command, ProtocolError> {
1084 if args.len() != 1 {
1085 return Err(ProtocolError::WrongArity("HLEN".into()));
1086 }
1087 let key = extract_string(&args[0])?;
1088 Ok(Command::HLen { key })
1089}
1090
1091fn parse_hincrby(args: &[Frame]) -> Result<Command, ProtocolError> {
1092 if args.len() != 3 {
1093 return Err(ProtocolError::WrongArity("HINCRBY".into()));
1094 }
1095 let key = extract_string(&args[0])?;
1096 let field = extract_string(&args[1])?;
1097 let delta = parse_i64(&args[2], "HINCRBY")?;
1098 Ok(Command::HIncrBy { key, field, delta })
1099}
1100
1101fn parse_hkeys(args: &[Frame]) -> Result<Command, ProtocolError> {
1102 if args.len() != 1 {
1103 return Err(ProtocolError::WrongArity("HKEYS".into()));
1104 }
1105 let key = extract_string(&args[0])?;
1106 Ok(Command::HKeys { key })
1107}
1108
1109fn parse_hvals(args: &[Frame]) -> Result<Command, ProtocolError> {
1110 if args.len() != 1 {
1111 return Err(ProtocolError::WrongArity("HVALS".into()));
1112 }
1113 let key = extract_string(&args[0])?;
1114 Ok(Command::HVals { key })
1115}
1116
1117fn parse_hmget(args: &[Frame]) -> Result<Command, ProtocolError> {
1118 if args.len() < 2 {
1119 return Err(ProtocolError::WrongArity("HMGET".into()));
1120 }
1121 let key = extract_string(&args[0])?;
1122 let fields = args[1..]
1123 .iter()
1124 .map(extract_string)
1125 .collect::<Result<Vec<_>, _>>()?;
1126 Ok(Command::HMGet { key, fields })
1127}
1128
1129fn parse_sadd(args: &[Frame]) -> Result<Command, ProtocolError> {
1132 if args.len() < 2 {
1133 return Err(ProtocolError::WrongArity("SADD".into()));
1134 }
1135 let key = extract_string(&args[0])?;
1136 let members = args[1..]
1137 .iter()
1138 .map(extract_string)
1139 .collect::<Result<Vec<_>, _>>()?;
1140 Ok(Command::SAdd { key, members })
1141}
1142
1143fn parse_srem(args: &[Frame]) -> Result<Command, ProtocolError> {
1144 if args.len() < 2 {
1145 return Err(ProtocolError::WrongArity("SREM".into()));
1146 }
1147 let key = extract_string(&args[0])?;
1148 let members = args[1..]
1149 .iter()
1150 .map(extract_string)
1151 .collect::<Result<Vec<_>, _>>()?;
1152 Ok(Command::SRem { key, members })
1153}
1154
1155fn parse_smembers(args: &[Frame]) -> Result<Command, ProtocolError> {
1156 if args.len() != 1 {
1157 return Err(ProtocolError::WrongArity("SMEMBERS".into()));
1158 }
1159 let key = extract_string(&args[0])?;
1160 Ok(Command::SMembers { key })
1161}
1162
1163fn parse_sismember(args: &[Frame]) -> Result<Command, ProtocolError> {
1164 if args.len() != 2 {
1165 return Err(ProtocolError::WrongArity("SISMEMBER".into()));
1166 }
1167 let key = extract_string(&args[0])?;
1168 let member = extract_string(&args[1])?;
1169 Ok(Command::SIsMember { key, member })
1170}
1171
1172fn parse_scard(args: &[Frame]) -> Result<Command, ProtocolError> {
1173 if args.len() != 1 {
1174 return Err(ProtocolError::WrongArity("SCARD".into()));
1175 }
1176 let key = extract_string(&args[0])?;
1177 Ok(Command::SCard { key })
1178}
1179
1180fn parse_cluster(args: &[Frame]) -> Result<Command, ProtocolError> {
1183 if args.is_empty() {
1184 return Err(ProtocolError::WrongArity("CLUSTER".into()));
1185 }
1186
1187 let subcommand = extract_string(&args[0])?.to_ascii_uppercase();
1188 match subcommand.as_str() {
1189 "INFO" => {
1190 if args.len() != 1 {
1191 return Err(ProtocolError::WrongArity("CLUSTER INFO".into()));
1192 }
1193 Ok(Command::ClusterInfo)
1194 }
1195 "NODES" => {
1196 if args.len() != 1 {
1197 return Err(ProtocolError::WrongArity("CLUSTER NODES".into()));
1198 }
1199 Ok(Command::ClusterNodes)
1200 }
1201 "SLOTS" => {
1202 if args.len() != 1 {
1203 return Err(ProtocolError::WrongArity("CLUSTER SLOTS".into()));
1204 }
1205 Ok(Command::ClusterSlots)
1206 }
1207 "KEYSLOT" => {
1208 if args.len() != 2 {
1209 return Err(ProtocolError::WrongArity("CLUSTER KEYSLOT".into()));
1210 }
1211 let key = extract_string(&args[1])?;
1212 Ok(Command::ClusterKeySlot { key })
1213 }
1214 "MYID" => {
1215 if args.len() != 1 {
1216 return Err(ProtocolError::WrongArity("CLUSTER MYID".into()));
1217 }
1218 Ok(Command::ClusterMyId)
1219 }
1220 "SETSLOT" => parse_cluster_setslot(&args[1..]),
1221 "MEET" => {
1222 if args.len() != 3 {
1223 return Err(ProtocolError::WrongArity("CLUSTER MEET".into()));
1224 }
1225 let ip = extract_string(&args[1])?;
1226 let port_str = extract_string(&args[2])?;
1227 let port: u16 = port_str
1228 .parse()
1229 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid port number".into()))?;
1230 Ok(Command::ClusterMeet { ip, port })
1231 }
1232 "ADDSLOTS" => {
1233 if args.len() < 2 {
1234 return Err(ProtocolError::WrongArity("CLUSTER ADDSLOTS".into()));
1235 }
1236 let slots = parse_slot_list(&args[1..])?;
1237 Ok(Command::ClusterAddSlots { slots })
1238 }
1239 "DELSLOTS" => {
1240 if args.len() < 2 {
1241 return Err(ProtocolError::WrongArity("CLUSTER DELSLOTS".into()));
1242 }
1243 let slots = parse_slot_list(&args[1..])?;
1244 Ok(Command::ClusterDelSlots { slots })
1245 }
1246 "FORGET" => {
1247 if args.len() != 2 {
1248 return Err(ProtocolError::WrongArity("CLUSTER FORGET".into()));
1249 }
1250 let node_id = extract_string(&args[1])?;
1251 Ok(Command::ClusterForget { node_id })
1252 }
1253 "REPLICATE" => {
1254 if args.len() != 2 {
1255 return Err(ProtocolError::WrongArity("CLUSTER REPLICATE".into()));
1256 }
1257 let node_id = extract_string(&args[1])?;
1258 Ok(Command::ClusterReplicate { node_id })
1259 }
1260 "FAILOVER" => {
1261 let mut force = false;
1262 let mut takeover = false;
1263 for arg in &args[1..] {
1264 let opt = extract_string(arg)?.to_ascii_uppercase();
1265 match opt.as_str() {
1266 "FORCE" => force = true,
1267 "TAKEOVER" => takeover = true,
1268 _ => {
1269 return Err(ProtocolError::InvalidCommandFrame(format!(
1270 "unknown CLUSTER FAILOVER option '{opt}'"
1271 )))
1272 }
1273 }
1274 }
1275 Ok(Command::ClusterFailover { force, takeover })
1276 }
1277 "COUNTKEYSINSLOT" => {
1278 if args.len() != 2 {
1279 return Err(ProtocolError::WrongArity("CLUSTER COUNTKEYSINSLOT".into()));
1280 }
1281 let slot_str = extract_string(&args[1])?;
1282 let slot: u16 = slot_str
1283 .parse()
1284 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1285 Ok(Command::ClusterCountKeysInSlot { slot })
1286 }
1287 "GETKEYSINSLOT" => {
1288 if args.len() != 3 {
1289 return Err(ProtocolError::WrongArity("CLUSTER GETKEYSINSLOT".into()));
1290 }
1291 let slot_str = extract_string(&args[1])?;
1292 let slot: u16 = slot_str
1293 .parse()
1294 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1295 let count_str = extract_string(&args[2])?;
1296 let count: u32 = count_str
1297 .parse()
1298 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid count".into()))?;
1299 Ok(Command::ClusterGetKeysInSlot { slot, count })
1300 }
1301 _ => Err(ProtocolError::InvalidCommandFrame(format!(
1302 "unknown CLUSTER subcommand '{subcommand}'"
1303 ))),
1304 }
1305}
1306
1307fn parse_asking(args: &[Frame]) -> Result<Command, ProtocolError> {
1308 if !args.is_empty() {
1309 return Err(ProtocolError::WrongArity("ASKING".into()));
1310 }
1311 Ok(Command::Asking)
1312}
1313
1314fn parse_slowlog(args: &[Frame]) -> Result<Command, ProtocolError> {
1315 if args.is_empty() {
1316 return Err(ProtocolError::WrongArity("SLOWLOG".into()));
1317 }
1318
1319 let subcmd = extract_string(&args[0])?.to_ascii_uppercase();
1320 match subcmd.as_str() {
1321 "GET" => {
1322 let count = if args.len() > 1 {
1323 let n: usize = extract_string(&args[1])?.parse().map_err(|_| {
1324 ProtocolError::InvalidCommandFrame("invalid count for SLOWLOG GET".into())
1325 })?;
1326 Some(n)
1327 } else {
1328 None
1329 };
1330 Ok(Command::SlowLogGet { count })
1331 }
1332 "LEN" => Ok(Command::SlowLogLen),
1333 "RESET" => Ok(Command::SlowLogReset),
1334 other => Err(ProtocolError::InvalidCommandFrame(format!(
1335 "unknown SLOWLOG subcommand '{other}'"
1336 ))),
1337 }
1338}
1339
1340fn parse_slot_list(args: &[Frame]) -> Result<Vec<u16>, ProtocolError> {
1341 let mut slots = Vec::with_capacity(args.len());
1342 for arg in args {
1343 let slot_str = extract_string(arg)?;
1344 let slot: u16 = slot_str
1345 .parse()
1346 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1347 slots.push(slot);
1348 }
1349 Ok(slots)
1350}
1351
1352fn parse_cluster_setslot(args: &[Frame]) -> Result<Command, ProtocolError> {
1353 if args.is_empty() {
1354 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT".into()));
1355 }
1356
1357 let slot_str = extract_string(&args[0])?;
1358 let slot: u16 = slot_str
1359 .parse()
1360 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1361
1362 if args.len() < 2 {
1363 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT".into()));
1364 }
1365
1366 let action = extract_string(&args[1])?.to_ascii_uppercase();
1367 match action.as_str() {
1368 "IMPORTING" => {
1369 if args.len() != 3 {
1370 return Err(ProtocolError::WrongArity(
1371 "CLUSTER SETSLOT IMPORTING".into(),
1372 ));
1373 }
1374 let node_id = extract_string(&args[2])?;
1375 Ok(Command::ClusterSetSlotImporting { slot, node_id })
1376 }
1377 "MIGRATING" => {
1378 if args.len() != 3 {
1379 return Err(ProtocolError::WrongArity(
1380 "CLUSTER SETSLOT MIGRATING".into(),
1381 ));
1382 }
1383 let node_id = extract_string(&args[2])?;
1384 Ok(Command::ClusterSetSlotMigrating { slot, node_id })
1385 }
1386 "NODE" => {
1387 if args.len() != 3 {
1388 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT NODE".into()));
1389 }
1390 let node_id = extract_string(&args[2])?;
1391 Ok(Command::ClusterSetSlotNode { slot, node_id })
1392 }
1393 "STABLE" => {
1394 if args.len() != 2 {
1395 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT STABLE".into()));
1396 }
1397 Ok(Command::ClusterSetSlotStable { slot })
1398 }
1399 _ => Err(ProtocolError::InvalidCommandFrame(format!(
1400 "unknown CLUSTER SETSLOT action '{action}'"
1401 ))),
1402 }
1403}
1404
1405fn parse_migrate(args: &[Frame]) -> Result<Command, ProtocolError> {
1406 if args.len() < 5 {
1408 return Err(ProtocolError::WrongArity("MIGRATE".into()));
1409 }
1410
1411 let host = extract_string(&args[0])?;
1412 let port_str = extract_string(&args[1])?;
1413 let port: u16 = port_str
1414 .parse()
1415 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid port number".into()))?;
1416 let key = extract_string(&args[2])?;
1417 let db_str = extract_string(&args[3])?;
1418 let db: u32 = db_str
1419 .parse()
1420 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid db number".into()))?;
1421 let timeout_str = extract_string(&args[4])?;
1422 let timeout_ms: u64 = timeout_str
1423 .parse()
1424 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid timeout".into()))?;
1425
1426 let mut copy = false;
1427 let mut replace = false;
1428
1429 for arg in &args[5..] {
1430 let opt = extract_string(arg)?.to_ascii_uppercase();
1431 match opt.as_str() {
1432 "COPY" => copy = true,
1433 "REPLACE" => replace = true,
1434 _ => {
1435 return Err(ProtocolError::InvalidCommandFrame(format!(
1436 "unknown MIGRATE option '{opt}'"
1437 )))
1438 }
1439 }
1440 }
1441
1442 Ok(Command::Migrate {
1443 host,
1444 port,
1445 key,
1446 db,
1447 timeout_ms,
1448 copy,
1449 replace,
1450 })
1451}
1452
1453#[cfg(test)]
1454mod tests {
1455 use super::*;
1456
1457 fn cmd(parts: &[&str]) -> Frame {
1459 Frame::Array(
1460 parts
1461 .iter()
1462 .map(|s| Frame::Bulk(Bytes::from(s.to_string())))
1463 .collect(),
1464 )
1465 }
1466
1467 #[test]
1470 fn ping_no_args() {
1471 assert_eq!(
1472 Command::from_frame(cmd(&["PING"])).unwrap(),
1473 Command::Ping(None),
1474 );
1475 }
1476
1477 #[test]
1478 fn ping_with_message() {
1479 assert_eq!(
1480 Command::from_frame(cmd(&["PING", "hello"])).unwrap(),
1481 Command::Ping(Some(Bytes::from("hello"))),
1482 );
1483 }
1484
1485 #[test]
1486 fn ping_case_insensitive() {
1487 assert_eq!(
1488 Command::from_frame(cmd(&["ping"])).unwrap(),
1489 Command::Ping(None),
1490 );
1491 assert_eq!(
1492 Command::from_frame(cmd(&["Ping"])).unwrap(),
1493 Command::Ping(None),
1494 );
1495 }
1496
1497 #[test]
1498 fn ping_too_many_args() {
1499 let err = Command::from_frame(cmd(&["PING", "a", "b"])).unwrap_err();
1500 assert!(matches!(err, ProtocolError::WrongArity(_)));
1501 }
1502
1503 #[test]
1506 fn echo() {
1507 assert_eq!(
1508 Command::from_frame(cmd(&["ECHO", "test"])).unwrap(),
1509 Command::Echo(Bytes::from("test")),
1510 );
1511 }
1512
1513 #[test]
1514 fn echo_missing_arg() {
1515 let err = Command::from_frame(cmd(&["ECHO"])).unwrap_err();
1516 assert!(matches!(err, ProtocolError::WrongArity(_)));
1517 }
1518
1519 #[test]
1522 fn get_basic() {
1523 assert_eq!(
1524 Command::from_frame(cmd(&["GET", "mykey"])).unwrap(),
1525 Command::Get {
1526 key: "mykey".into()
1527 },
1528 );
1529 }
1530
1531 #[test]
1532 fn get_no_args() {
1533 let err = Command::from_frame(cmd(&["GET"])).unwrap_err();
1534 assert!(matches!(err, ProtocolError::WrongArity(_)));
1535 }
1536
1537 #[test]
1538 fn get_too_many_args() {
1539 let err = Command::from_frame(cmd(&["GET", "a", "b"])).unwrap_err();
1540 assert!(matches!(err, ProtocolError::WrongArity(_)));
1541 }
1542
1543 #[test]
1544 fn get_case_insensitive() {
1545 assert_eq!(
1546 Command::from_frame(cmd(&["get", "k"])).unwrap(),
1547 Command::Get { key: "k".into() },
1548 );
1549 }
1550
1551 #[test]
1554 fn set_basic() {
1555 assert_eq!(
1556 Command::from_frame(cmd(&["SET", "key", "value"])).unwrap(),
1557 Command::Set {
1558 key: "key".into(),
1559 value: Bytes::from("value"),
1560 expire: None,
1561 nx: false,
1562 xx: false,
1563 },
1564 );
1565 }
1566
1567 #[test]
1568 fn set_with_ex() {
1569 assert_eq!(
1570 Command::from_frame(cmd(&["SET", "key", "val", "EX", "10"])).unwrap(),
1571 Command::Set {
1572 key: "key".into(),
1573 value: Bytes::from("val"),
1574 expire: Some(SetExpire::Ex(10)),
1575 nx: false,
1576 xx: false,
1577 },
1578 );
1579 }
1580
1581 #[test]
1582 fn set_with_px() {
1583 assert_eq!(
1584 Command::from_frame(cmd(&["SET", "key", "val", "PX", "5000"])).unwrap(),
1585 Command::Set {
1586 key: "key".into(),
1587 value: Bytes::from("val"),
1588 expire: Some(SetExpire::Px(5000)),
1589 nx: false,
1590 xx: false,
1591 },
1592 );
1593 }
1594
1595 #[test]
1596 fn set_ex_case_insensitive() {
1597 assert_eq!(
1598 Command::from_frame(cmd(&["set", "k", "v", "ex", "5"])).unwrap(),
1599 Command::Set {
1600 key: "k".into(),
1601 value: Bytes::from("v"),
1602 expire: Some(SetExpire::Ex(5)),
1603 nx: false,
1604 xx: false,
1605 },
1606 );
1607 }
1608
1609 #[test]
1610 fn set_nx_flag() {
1611 assert_eq!(
1612 Command::from_frame(cmd(&["SET", "key", "val", "NX"])).unwrap(),
1613 Command::Set {
1614 key: "key".into(),
1615 value: Bytes::from("val"),
1616 expire: None,
1617 nx: true,
1618 xx: false,
1619 },
1620 );
1621 }
1622
1623 #[test]
1624 fn set_xx_flag() {
1625 assert_eq!(
1626 Command::from_frame(cmd(&["SET", "key", "val", "XX"])).unwrap(),
1627 Command::Set {
1628 key: "key".into(),
1629 value: Bytes::from("val"),
1630 expire: None,
1631 nx: false,
1632 xx: true,
1633 },
1634 );
1635 }
1636
1637 #[test]
1638 fn set_nx_with_ex() {
1639 assert_eq!(
1640 Command::from_frame(cmd(&["SET", "key", "val", "EX", "10", "NX"])).unwrap(),
1641 Command::Set {
1642 key: "key".into(),
1643 value: Bytes::from("val"),
1644 expire: Some(SetExpire::Ex(10)),
1645 nx: true,
1646 xx: false,
1647 },
1648 );
1649 }
1650
1651 #[test]
1652 fn set_nx_before_ex() {
1653 assert_eq!(
1654 Command::from_frame(cmd(&["SET", "key", "val", "NX", "PX", "5000"])).unwrap(),
1655 Command::Set {
1656 key: "key".into(),
1657 value: Bytes::from("val"),
1658 expire: Some(SetExpire::Px(5000)),
1659 nx: true,
1660 xx: false,
1661 },
1662 );
1663 }
1664
1665 #[test]
1666 fn set_nx_xx_conflict() {
1667 let err = Command::from_frame(cmd(&["SET", "k", "v", "NX", "XX"])).unwrap_err();
1668 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1669 }
1670
1671 #[test]
1672 fn set_nx_case_insensitive() {
1673 assert_eq!(
1674 Command::from_frame(cmd(&["set", "k", "v", "nx"])).unwrap(),
1675 Command::Set {
1676 key: "k".into(),
1677 value: Bytes::from("v"),
1678 expire: None,
1679 nx: true,
1680 xx: false,
1681 },
1682 );
1683 }
1684
1685 #[test]
1686 fn set_missing_value() {
1687 let err = Command::from_frame(cmd(&["SET", "key"])).unwrap_err();
1688 assert!(matches!(err, ProtocolError::WrongArity(_)));
1689 }
1690
1691 #[test]
1692 fn set_invalid_expire_value() {
1693 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX", "notanum"])).unwrap_err();
1694 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1695 }
1696
1697 #[test]
1698 fn set_zero_expire() {
1699 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX", "0"])).unwrap_err();
1700 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1701 }
1702
1703 #[test]
1704 fn set_unknown_flag() {
1705 let err = Command::from_frame(cmd(&["SET", "k", "v", "ZZ", "10"])).unwrap_err();
1706 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1707 }
1708
1709 #[test]
1710 fn set_incomplete_expire() {
1711 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX"])).unwrap_err();
1713 assert!(matches!(err, ProtocolError::WrongArity(_)));
1714 }
1715
1716 #[test]
1719 fn del_single() {
1720 assert_eq!(
1721 Command::from_frame(cmd(&["DEL", "key"])).unwrap(),
1722 Command::Del {
1723 keys: vec!["key".into()]
1724 },
1725 );
1726 }
1727
1728 #[test]
1729 fn del_multiple() {
1730 assert_eq!(
1731 Command::from_frame(cmd(&["DEL", "a", "b", "c"])).unwrap(),
1732 Command::Del {
1733 keys: vec!["a".into(), "b".into(), "c".into()]
1734 },
1735 );
1736 }
1737
1738 #[test]
1739 fn del_no_args() {
1740 let err = Command::from_frame(cmd(&["DEL"])).unwrap_err();
1741 assert!(matches!(err, ProtocolError::WrongArity(_)));
1742 }
1743
1744 #[test]
1747 fn exists_single() {
1748 assert_eq!(
1749 Command::from_frame(cmd(&["EXISTS", "key"])).unwrap(),
1750 Command::Exists {
1751 keys: vec!["key".into()]
1752 },
1753 );
1754 }
1755
1756 #[test]
1757 fn exists_multiple() {
1758 assert_eq!(
1759 Command::from_frame(cmd(&["EXISTS", "a", "b"])).unwrap(),
1760 Command::Exists {
1761 keys: vec!["a".into(), "b".into()]
1762 },
1763 );
1764 }
1765
1766 #[test]
1767 fn exists_no_args() {
1768 let err = Command::from_frame(cmd(&["EXISTS"])).unwrap_err();
1769 assert!(matches!(err, ProtocolError::WrongArity(_)));
1770 }
1771
1772 #[test]
1775 fn mget_single() {
1776 assert_eq!(
1777 Command::from_frame(cmd(&["MGET", "key"])).unwrap(),
1778 Command::MGet {
1779 keys: vec!["key".into()]
1780 },
1781 );
1782 }
1783
1784 #[test]
1785 fn mget_multiple() {
1786 assert_eq!(
1787 Command::from_frame(cmd(&["MGET", "a", "b", "c"])).unwrap(),
1788 Command::MGet {
1789 keys: vec!["a".into(), "b".into(), "c".into()]
1790 },
1791 );
1792 }
1793
1794 #[test]
1795 fn mget_no_args() {
1796 let err = Command::from_frame(cmd(&["MGET"])).unwrap_err();
1797 assert!(matches!(err, ProtocolError::WrongArity(_)));
1798 }
1799
1800 #[test]
1803 fn mset_single_pair() {
1804 assert_eq!(
1805 Command::from_frame(cmd(&["MSET", "key", "val"])).unwrap(),
1806 Command::MSet {
1807 pairs: vec![("key".into(), Bytes::from("val"))]
1808 },
1809 );
1810 }
1811
1812 #[test]
1813 fn mset_multiple_pairs() {
1814 assert_eq!(
1815 Command::from_frame(cmd(&["MSET", "a", "1", "b", "2"])).unwrap(),
1816 Command::MSet {
1817 pairs: vec![
1818 ("a".into(), Bytes::from("1")),
1819 ("b".into(), Bytes::from("2")),
1820 ]
1821 },
1822 );
1823 }
1824
1825 #[test]
1826 fn mset_no_args() {
1827 let err = Command::from_frame(cmd(&["MSET"])).unwrap_err();
1828 assert!(matches!(err, ProtocolError::WrongArity(_)));
1829 }
1830
1831 #[test]
1832 fn mset_odd_args() {
1833 let err = Command::from_frame(cmd(&["MSET", "a", "1", "b"])).unwrap_err();
1835 assert!(matches!(err, ProtocolError::WrongArity(_)));
1836 }
1837
1838 #[test]
1841 fn expire_basic() {
1842 assert_eq!(
1843 Command::from_frame(cmd(&["EXPIRE", "key", "60"])).unwrap(),
1844 Command::Expire {
1845 key: "key".into(),
1846 seconds: 60,
1847 },
1848 );
1849 }
1850
1851 #[test]
1852 fn expire_wrong_arity() {
1853 let err = Command::from_frame(cmd(&["EXPIRE", "key"])).unwrap_err();
1854 assert!(matches!(err, ProtocolError::WrongArity(_)));
1855 }
1856
1857 #[test]
1858 fn expire_invalid_seconds() {
1859 let err = Command::from_frame(cmd(&["EXPIRE", "key", "abc"])).unwrap_err();
1860 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1861 }
1862
1863 #[test]
1864 fn expire_zero_seconds() {
1865 let err = Command::from_frame(cmd(&["EXPIRE", "key", "0"])).unwrap_err();
1866 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1867 }
1868
1869 #[test]
1872 fn ttl_basic() {
1873 assert_eq!(
1874 Command::from_frame(cmd(&["TTL", "key"])).unwrap(),
1875 Command::Ttl { key: "key".into() },
1876 );
1877 }
1878
1879 #[test]
1880 fn ttl_wrong_arity() {
1881 let err = Command::from_frame(cmd(&["TTL"])).unwrap_err();
1882 assert!(matches!(err, ProtocolError::WrongArity(_)));
1883 }
1884
1885 #[test]
1888 fn dbsize_basic() {
1889 assert_eq!(
1890 Command::from_frame(cmd(&["DBSIZE"])).unwrap(),
1891 Command::DbSize,
1892 );
1893 }
1894
1895 #[test]
1896 fn dbsize_case_insensitive() {
1897 assert_eq!(
1898 Command::from_frame(cmd(&["dbsize"])).unwrap(),
1899 Command::DbSize,
1900 );
1901 }
1902
1903 #[test]
1904 fn dbsize_extra_args() {
1905 let err = Command::from_frame(cmd(&["DBSIZE", "extra"])).unwrap_err();
1906 assert!(matches!(err, ProtocolError::WrongArity(_)));
1907 }
1908
1909 #[test]
1912 fn info_no_section() {
1913 assert_eq!(
1914 Command::from_frame(cmd(&["INFO"])).unwrap(),
1915 Command::Info { section: None },
1916 );
1917 }
1918
1919 #[test]
1920 fn info_with_section() {
1921 assert_eq!(
1922 Command::from_frame(cmd(&["INFO", "keyspace"])).unwrap(),
1923 Command::Info {
1924 section: Some("keyspace".into())
1925 },
1926 );
1927 }
1928
1929 #[test]
1930 fn info_too_many_args() {
1931 let err = Command::from_frame(cmd(&["INFO", "a", "b"])).unwrap_err();
1932 assert!(matches!(err, ProtocolError::WrongArity(_)));
1933 }
1934
1935 #[test]
1938 fn bgsave_basic() {
1939 assert_eq!(
1940 Command::from_frame(cmd(&["BGSAVE"])).unwrap(),
1941 Command::BgSave,
1942 );
1943 }
1944
1945 #[test]
1946 fn bgsave_case_insensitive() {
1947 assert_eq!(
1948 Command::from_frame(cmd(&["bgsave"])).unwrap(),
1949 Command::BgSave,
1950 );
1951 }
1952
1953 #[test]
1954 fn bgsave_extra_args() {
1955 let err = Command::from_frame(cmd(&["BGSAVE", "extra"])).unwrap_err();
1956 assert!(matches!(err, ProtocolError::WrongArity(_)));
1957 }
1958
1959 #[test]
1962 fn bgrewriteaof_basic() {
1963 assert_eq!(
1964 Command::from_frame(cmd(&["BGREWRITEAOF"])).unwrap(),
1965 Command::BgRewriteAof,
1966 );
1967 }
1968
1969 #[test]
1970 fn bgrewriteaof_case_insensitive() {
1971 assert_eq!(
1972 Command::from_frame(cmd(&["bgrewriteaof"])).unwrap(),
1973 Command::BgRewriteAof,
1974 );
1975 }
1976
1977 #[test]
1978 fn bgrewriteaof_extra_args() {
1979 let err = Command::from_frame(cmd(&["BGREWRITEAOF", "extra"])).unwrap_err();
1980 assert!(matches!(err, ProtocolError::WrongArity(_)));
1981 }
1982
1983 #[test]
1986 fn flushdb_basic() {
1987 assert_eq!(
1988 Command::from_frame(cmd(&["FLUSHDB"])).unwrap(),
1989 Command::FlushDb,
1990 );
1991 }
1992
1993 #[test]
1994 fn flushdb_case_insensitive() {
1995 assert_eq!(
1996 Command::from_frame(cmd(&["flushdb"])).unwrap(),
1997 Command::FlushDb,
1998 );
1999 }
2000
2001 #[test]
2002 fn flushdb_extra_args() {
2003 let err = Command::from_frame(cmd(&["FLUSHDB", "extra"])).unwrap_err();
2004 assert!(matches!(err, ProtocolError::WrongArity(_)));
2005 }
2006
2007 #[test]
2010 fn lpush_single() {
2011 assert_eq!(
2012 Command::from_frame(cmd(&["LPUSH", "list", "val"])).unwrap(),
2013 Command::LPush {
2014 key: "list".into(),
2015 values: vec![Bytes::from("val")],
2016 },
2017 );
2018 }
2019
2020 #[test]
2021 fn lpush_multiple() {
2022 assert_eq!(
2023 Command::from_frame(cmd(&["LPUSH", "list", "a", "b", "c"])).unwrap(),
2024 Command::LPush {
2025 key: "list".into(),
2026 values: vec![Bytes::from("a"), Bytes::from("b"), Bytes::from("c")],
2027 },
2028 );
2029 }
2030
2031 #[test]
2032 fn lpush_no_value() {
2033 let err = Command::from_frame(cmd(&["LPUSH", "key"])).unwrap_err();
2034 assert!(matches!(err, ProtocolError::WrongArity(_)));
2035 }
2036
2037 #[test]
2038 fn lpush_case_insensitive() {
2039 assert!(matches!(
2040 Command::from_frame(cmd(&["lpush", "k", "v"])).unwrap(),
2041 Command::LPush { .. }
2042 ));
2043 }
2044
2045 #[test]
2048 fn rpush_single() {
2049 assert_eq!(
2050 Command::from_frame(cmd(&["RPUSH", "list", "val"])).unwrap(),
2051 Command::RPush {
2052 key: "list".into(),
2053 values: vec![Bytes::from("val")],
2054 },
2055 );
2056 }
2057
2058 #[test]
2059 fn rpush_no_value() {
2060 let err = Command::from_frame(cmd(&["RPUSH", "key"])).unwrap_err();
2061 assert!(matches!(err, ProtocolError::WrongArity(_)));
2062 }
2063
2064 #[test]
2067 fn lpop_basic() {
2068 assert_eq!(
2069 Command::from_frame(cmd(&["LPOP", "list"])).unwrap(),
2070 Command::LPop { key: "list".into() },
2071 );
2072 }
2073
2074 #[test]
2075 fn lpop_wrong_arity() {
2076 let err = Command::from_frame(cmd(&["LPOP"])).unwrap_err();
2077 assert!(matches!(err, ProtocolError::WrongArity(_)));
2078 }
2079
2080 #[test]
2083 fn rpop_basic() {
2084 assert_eq!(
2085 Command::from_frame(cmd(&["RPOP", "list"])).unwrap(),
2086 Command::RPop { key: "list".into() },
2087 );
2088 }
2089
2090 #[test]
2093 fn lrange_basic() {
2094 assert_eq!(
2095 Command::from_frame(cmd(&["LRANGE", "list", "0", "-1"])).unwrap(),
2096 Command::LRange {
2097 key: "list".into(),
2098 start: 0,
2099 stop: -1,
2100 },
2101 );
2102 }
2103
2104 #[test]
2105 fn lrange_wrong_arity() {
2106 let err = Command::from_frame(cmd(&["LRANGE", "list", "0"])).unwrap_err();
2107 assert!(matches!(err, ProtocolError::WrongArity(_)));
2108 }
2109
2110 #[test]
2111 fn lrange_invalid_index() {
2112 let err = Command::from_frame(cmd(&["LRANGE", "list", "abc", "0"])).unwrap_err();
2113 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2114 }
2115
2116 #[test]
2119 fn llen_basic() {
2120 assert_eq!(
2121 Command::from_frame(cmd(&["LLEN", "list"])).unwrap(),
2122 Command::LLen { key: "list".into() },
2123 );
2124 }
2125
2126 #[test]
2127 fn llen_wrong_arity() {
2128 let err = Command::from_frame(cmd(&["LLEN"])).unwrap_err();
2129 assert!(matches!(err, ProtocolError::WrongArity(_)));
2130 }
2131
2132 #[test]
2135 fn type_basic() {
2136 assert_eq!(
2137 Command::from_frame(cmd(&["TYPE", "key"])).unwrap(),
2138 Command::Type { key: "key".into() },
2139 );
2140 }
2141
2142 #[test]
2143 fn type_wrong_arity() {
2144 let err = Command::from_frame(cmd(&["TYPE"])).unwrap_err();
2145 assert!(matches!(err, ProtocolError::WrongArity(_)));
2146 }
2147
2148 #[test]
2149 fn type_case_insensitive() {
2150 assert!(matches!(
2151 Command::from_frame(cmd(&["type", "k"])).unwrap(),
2152 Command::Type { .. }
2153 ));
2154 }
2155
2156 #[test]
2159 fn unknown_command() {
2160 assert_eq!(
2161 Command::from_frame(cmd(&["FOOBAR", "arg"])).unwrap(),
2162 Command::Unknown("FOOBAR".into()),
2163 );
2164 }
2165
2166 #[test]
2167 fn non_array_frame() {
2168 let err = Command::from_frame(Frame::Simple("PING".into())).unwrap_err();
2169 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2170 }
2171
2172 #[test]
2173 fn empty_array() {
2174 let err = Command::from_frame(Frame::Array(vec![])).unwrap_err();
2175 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2176 }
2177
2178 #[test]
2181 fn zadd_basic() {
2182 let parsed = Command::from_frame(cmd(&["ZADD", "board", "100", "alice"])).unwrap();
2183 match parsed {
2184 Command::ZAdd {
2185 key,
2186 flags,
2187 members,
2188 } => {
2189 assert_eq!(key, "board");
2190 assert_eq!(flags, ZAddFlags::default());
2191 assert_eq!(members, vec![(100.0, "alice".into())]);
2192 }
2193 other => panic!("expected ZAdd, got {other:?}"),
2194 }
2195 }
2196
2197 #[test]
2198 fn zadd_multiple_members() {
2199 let parsed =
2200 Command::from_frame(cmd(&["ZADD", "board", "100", "alice", "200", "bob"])).unwrap();
2201 match parsed {
2202 Command::ZAdd { members, .. } => {
2203 assert_eq!(members.len(), 2);
2204 assert_eq!(members[0], (100.0, "alice".into()));
2205 assert_eq!(members[1], (200.0, "bob".into()));
2206 }
2207 other => panic!("expected ZAdd, got {other:?}"),
2208 }
2209 }
2210
2211 #[test]
2212 fn zadd_with_flags() {
2213 let parsed = Command::from_frame(cmd(&["ZADD", "z", "NX", "CH", "100", "alice"])).unwrap();
2214 match parsed {
2215 Command::ZAdd { flags, .. } => {
2216 assert!(flags.nx);
2217 assert!(flags.ch);
2218 assert!(!flags.xx);
2219 assert!(!flags.gt);
2220 assert!(!flags.lt);
2221 }
2222 other => panic!("expected ZAdd, got {other:?}"),
2223 }
2224 }
2225
2226 #[test]
2227 fn zadd_gt_flag() {
2228 let parsed = Command::from_frame(cmd(&["zadd", "z", "gt", "100", "alice"])).unwrap();
2229 match parsed {
2230 Command::ZAdd { flags, .. } => assert!(flags.gt),
2231 other => panic!("expected ZAdd, got {other:?}"),
2232 }
2233 }
2234
2235 #[test]
2236 fn zadd_nx_xx_conflict() {
2237 let err = Command::from_frame(cmd(&["ZADD", "z", "NX", "XX", "100", "alice"])).unwrap_err();
2238 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2239 }
2240
2241 #[test]
2242 fn zadd_gt_lt_conflict() {
2243 let err = Command::from_frame(cmd(&["ZADD", "z", "GT", "LT", "100", "alice"])).unwrap_err();
2244 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2245 }
2246
2247 #[test]
2248 fn zadd_wrong_arity() {
2249 let err = Command::from_frame(cmd(&["ZADD", "z"])).unwrap_err();
2250 assert!(matches!(err, ProtocolError::WrongArity(_)));
2251 }
2252
2253 #[test]
2254 fn zadd_odd_score_member_count() {
2255 let err = Command::from_frame(cmd(&["ZADD", "z", "100"])).unwrap_err();
2257 assert!(matches!(err, ProtocolError::WrongArity(_)));
2258 }
2259
2260 #[test]
2261 fn zadd_invalid_score() {
2262 let err = Command::from_frame(cmd(&["ZADD", "z", "notanum", "alice"])).unwrap_err();
2263 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2264 }
2265
2266 #[test]
2267 fn zadd_nan_score() {
2268 let err = Command::from_frame(cmd(&["ZADD", "z", "nan", "alice"])).unwrap_err();
2269 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2270 }
2271
2272 #[test]
2273 fn zadd_negative_score() {
2274 let parsed = Command::from_frame(cmd(&["ZADD", "z", "-50.5", "alice"])).unwrap();
2275 match parsed {
2276 Command::ZAdd { members, .. } => {
2277 assert_eq!(members[0].0, -50.5);
2278 }
2279 other => panic!("expected ZAdd, got {other:?}"),
2280 }
2281 }
2282
2283 #[test]
2286 fn zrem_basic() {
2287 assert_eq!(
2288 Command::from_frame(cmd(&["ZREM", "z", "alice"])).unwrap(),
2289 Command::ZRem {
2290 key: "z".into(),
2291 members: vec!["alice".into()],
2292 },
2293 );
2294 }
2295
2296 #[test]
2297 fn zrem_multiple() {
2298 let parsed = Command::from_frame(cmd(&["ZREM", "z", "a", "b", "c"])).unwrap();
2299 match parsed {
2300 Command::ZRem { members, .. } => assert_eq!(members.len(), 3),
2301 other => panic!("expected ZRem, got {other:?}"),
2302 }
2303 }
2304
2305 #[test]
2306 fn zrem_wrong_arity() {
2307 let err = Command::from_frame(cmd(&["ZREM", "z"])).unwrap_err();
2308 assert!(matches!(err, ProtocolError::WrongArity(_)));
2309 }
2310
2311 #[test]
2314 fn zscore_basic() {
2315 assert_eq!(
2316 Command::from_frame(cmd(&["ZSCORE", "z", "alice"])).unwrap(),
2317 Command::ZScore {
2318 key: "z".into(),
2319 member: "alice".into(),
2320 },
2321 );
2322 }
2323
2324 #[test]
2325 fn zscore_wrong_arity() {
2326 let err = Command::from_frame(cmd(&["ZSCORE", "z"])).unwrap_err();
2327 assert!(matches!(err, ProtocolError::WrongArity(_)));
2328 }
2329
2330 #[test]
2333 fn zrank_basic() {
2334 assert_eq!(
2335 Command::from_frame(cmd(&["ZRANK", "z", "alice"])).unwrap(),
2336 Command::ZRank {
2337 key: "z".into(),
2338 member: "alice".into(),
2339 },
2340 );
2341 }
2342
2343 #[test]
2344 fn zrank_wrong_arity() {
2345 let err = Command::from_frame(cmd(&["ZRANK", "z"])).unwrap_err();
2346 assert!(matches!(err, ProtocolError::WrongArity(_)));
2347 }
2348
2349 #[test]
2352 fn zcard_basic() {
2353 assert_eq!(
2354 Command::from_frame(cmd(&["ZCARD", "z"])).unwrap(),
2355 Command::ZCard { key: "z".into() },
2356 );
2357 }
2358
2359 #[test]
2360 fn zcard_wrong_arity() {
2361 let err = Command::from_frame(cmd(&["ZCARD"])).unwrap_err();
2362 assert!(matches!(err, ProtocolError::WrongArity(_)));
2363 }
2364
2365 #[test]
2368 fn zrange_basic() {
2369 assert_eq!(
2370 Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1"])).unwrap(),
2371 Command::ZRange {
2372 key: "z".into(),
2373 start: 0,
2374 stop: -1,
2375 with_scores: false,
2376 },
2377 );
2378 }
2379
2380 #[test]
2381 fn zrange_with_scores() {
2382 assert_eq!(
2383 Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1", "WITHSCORES"])).unwrap(),
2384 Command::ZRange {
2385 key: "z".into(),
2386 start: 0,
2387 stop: -1,
2388 with_scores: true,
2389 },
2390 );
2391 }
2392
2393 #[test]
2394 fn zrange_withscores_case_insensitive() {
2395 assert_eq!(
2396 Command::from_frame(cmd(&["zrange", "z", "0", "-1", "withscores"])).unwrap(),
2397 Command::ZRange {
2398 key: "z".into(),
2399 start: 0,
2400 stop: -1,
2401 with_scores: true,
2402 },
2403 );
2404 }
2405
2406 #[test]
2407 fn zrange_invalid_option() {
2408 let err = Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1", "BADOPT"])).unwrap_err();
2409 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2410 }
2411
2412 #[test]
2413 fn zrange_wrong_arity() {
2414 let err = Command::from_frame(cmd(&["ZRANGE", "z", "0"])).unwrap_err();
2415 assert!(matches!(err, ProtocolError::WrongArity(_)));
2416 }
2417
2418 #[test]
2421 fn incr_basic() {
2422 assert_eq!(
2423 Command::from_frame(cmd(&["INCR", "counter"])).unwrap(),
2424 Command::Incr {
2425 key: "counter".into()
2426 },
2427 );
2428 }
2429
2430 #[test]
2431 fn incr_wrong_arity() {
2432 let err = Command::from_frame(cmd(&["INCR"])).unwrap_err();
2433 assert!(matches!(err, ProtocolError::WrongArity(_)));
2434 }
2435
2436 #[test]
2439 fn decr_basic() {
2440 assert_eq!(
2441 Command::from_frame(cmd(&["DECR", "counter"])).unwrap(),
2442 Command::Decr {
2443 key: "counter".into()
2444 },
2445 );
2446 }
2447
2448 #[test]
2449 fn decr_wrong_arity() {
2450 let err = Command::from_frame(cmd(&["DECR"])).unwrap_err();
2451 assert!(matches!(err, ProtocolError::WrongArity(_)));
2452 }
2453
2454 #[test]
2457 fn persist_basic() {
2458 assert_eq!(
2459 Command::from_frame(cmd(&["PERSIST", "key"])).unwrap(),
2460 Command::Persist { key: "key".into() },
2461 );
2462 }
2463
2464 #[test]
2465 fn persist_case_insensitive() {
2466 assert_eq!(
2467 Command::from_frame(cmd(&["persist", "key"])).unwrap(),
2468 Command::Persist { key: "key".into() },
2469 );
2470 }
2471
2472 #[test]
2473 fn persist_wrong_arity() {
2474 let err = Command::from_frame(cmd(&["PERSIST"])).unwrap_err();
2475 assert!(matches!(err, ProtocolError::WrongArity(_)));
2476 }
2477
2478 #[test]
2481 fn pttl_basic() {
2482 assert_eq!(
2483 Command::from_frame(cmd(&["PTTL", "key"])).unwrap(),
2484 Command::Pttl { key: "key".into() },
2485 );
2486 }
2487
2488 #[test]
2489 fn pttl_wrong_arity() {
2490 let err = Command::from_frame(cmd(&["PTTL"])).unwrap_err();
2491 assert!(matches!(err, ProtocolError::WrongArity(_)));
2492 }
2493
2494 #[test]
2497 fn pexpire_basic() {
2498 assert_eq!(
2499 Command::from_frame(cmd(&["PEXPIRE", "key", "5000"])).unwrap(),
2500 Command::Pexpire {
2501 key: "key".into(),
2502 milliseconds: 5000,
2503 },
2504 );
2505 }
2506
2507 #[test]
2508 fn pexpire_wrong_arity() {
2509 let err = Command::from_frame(cmd(&["PEXPIRE", "key"])).unwrap_err();
2510 assert!(matches!(err, ProtocolError::WrongArity(_)));
2511 }
2512
2513 #[test]
2514 fn pexpire_zero_millis() {
2515 let err = Command::from_frame(cmd(&["PEXPIRE", "key", "0"])).unwrap_err();
2516 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2517 }
2518
2519 #[test]
2520 fn pexpire_invalid_millis() {
2521 let err = Command::from_frame(cmd(&["PEXPIRE", "key", "notanum"])).unwrap_err();
2522 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2523 }
2524
2525 #[test]
2528 fn scan_basic() {
2529 assert_eq!(
2530 Command::from_frame(cmd(&["SCAN", "0"])).unwrap(),
2531 Command::Scan {
2532 cursor: 0,
2533 pattern: None,
2534 count: None,
2535 },
2536 );
2537 }
2538
2539 #[test]
2540 fn scan_with_match() {
2541 assert_eq!(
2542 Command::from_frame(cmd(&["SCAN", "0", "MATCH", "user:*"])).unwrap(),
2543 Command::Scan {
2544 cursor: 0,
2545 pattern: Some("user:*".into()),
2546 count: None,
2547 },
2548 );
2549 }
2550
2551 #[test]
2552 fn scan_with_count() {
2553 assert_eq!(
2554 Command::from_frame(cmd(&["SCAN", "42", "COUNT", "100"])).unwrap(),
2555 Command::Scan {
2556 cursor: 42,
2557 pattern: None,
2558 count: Some(100),
2559 },
2560 );
2561 }
2562
2563 #[test]
2564 fn scan_with_match_and_count() {
2565 assert_eq!(
2566 Command::from_frame(cmd(&["SCAN", "0", "MATCH", "*:data", "COUNT", "50"])).unwrap(),
2567 Command::Scan {
2568 cursor: 0,
2569 pattern: Some("*:data".into()),
2570 count: Some(50),
2571 },
2572 );
2573 }
2574
2575 #[test]
2576 fn scan_count_before_match() {
2577 assert_eq!(
2578 Command::from_frame(cmd(&["SCAN", "0", "COUNT", "10", "MATCH", "foo*"])).unwrap(),
2579 Command::Scan {
2580 cursor: 0,
2581 pattern: Some("foo*".into()),
2582 count: Some(10),
2583 },
2584 );
2585 }
2586
2587 #[test]
2588 fn scan_case_insensitive() {
2589 assert_eq!(
2590 Command::from_frame(cmd(&["scan", "0", "match", "x*", "count", "5"])).unwrap(),
2591 Command::Scan {
2592 cursor: 0,
2593 pattern: Some("x*".into()),
2594 count: Some(5),
2595 },
2596 );
2597 }
2598
2599 #[test]
2600 fn scan_wrong_arity() {
2601 let err = Command::from_frame(cmd(&["SCAN"])).unwrap_err();
2602 assert!(matches!(err, ProtocolError::WrongArity(_)));
2603 }
2604
2605 #[test]
2606 fn scan_invalid_cursor() {
2607 let err = Command::from_frame(cmd(&["SCAN", "notanum"])).unwrap_err();
2608 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2609 }
2610
2611 #[test]
2612 fn scan_invalid_count() {
2613 let err = Command::from_frame(cmd(&["SCAN", "0", "COUNT", "bad"])).unwrap_err();
2614 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2615 }
2616
2617 #[test]
2618 fn scan_unknown_flag() {
2619 let err = Command::from_frame(cmd(&["SCAN", "0", "BADOPT", "val"])).unwrap_err();
2620 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2621 }
2622
2623 #[test]
2624 fn scan_match_missing_pattern() {
2625 let err = Command::from_frame(cmd(&["SCAN", "0", "MATCH"])).unwrap_err();
2626 assert!(matches!(err, ProtocolError::WrongArity(_)));
2627 }
2628
2629 #[test]
2630 fn scan_count_missing_value() {
2631 let err = Command::from_frame(cmd(&["SCAN", "0", "COUNT"])).unwrap_err();
2632 assert!(matches!(err, ProtocolError::WrongArity(_)));
2633 }
2634
2635 #[test]
2638 fn hset_single_field() {
2639 assert_eq!(
2640 Command::from_frame(cmd(&["HSET", "h", "field", "value"])).unwrap(),
2641 Command::HSet {
2642 key: "h".into(),
2643 fields: vec![("field".into(), Bytes::from("value"))],
2644 },
2645 );
2646 }
2647
2648 #[test]
2649 fn hset_multiple_fields() {
2650 let parsed = Command::from_frame(cmd(&["HSET", "h", "f1", "v1", "f2", "v2"])).unwrap();
2651 match parsed {
2652 Command::HSet { key, fields } => {
2653 assert_eq!(key, "h");
2654 assert_eq!(fields.len(), 2);
2655 }
2656 other => panic!("expected HSet, got {other:?}"),
2657 }
2658 }
2659
2660 #[test]
2661 fn hset_wrong_arity() {
2662 let err = Command::from_frame(cmd(&["HSET", "h"])).unwrap_err();
2663 assert!(matches!(err, ProtocolError::WrongArity(_)));
2664 let err = Command::from_frame(cmd(&["HSET", "h", "f"])).unwrap_err();
2665 assert!(matches!(err, ProtocolError::WrongArity(_)));
2666 }
2667
2668 #[test]
2669 fn hget_basic() {
2670 assert_eq!(
2671 Command::from_frame(cmd(&["HGET", "h", "field"])).unwrap(),
2672 Command::HGet {
2673 key: "h".into(),
2674 field: "field".into(),
2675 },
2676 );
2677 }
2678
2679 #[test]
2680 fn hget_wrong_arity() {
2681 let err = Command::from_frame(cmd(&["HGET", "h"])).unwrap_err();
2682 assert!(matches!(err, ProtocolError::WrongArity(_)));
2683 }
2684
2685 #[test]
2686 fn hgetall_basic() {
2687 assert_eq!(
2688 Command::from_frame(cmd(&["HGETALL", "h"])).unwrap(),
2689 Command::HGetAll { key: "h".into() },
2690 );
2691 }
2692
2693 #[test]
2694 fn hgetall_wrong_arity() {
2695 let err = Command::from_frame(cmd(&["HGETALL"])).unwrap_err();
2696 assert!(matches!(err, ProtocolError::WrongArity(_)));
2697 }
2698
2699 #[test]
2700 fn hdel_single() {
2701 assert_eq!(
2702 Command::from_frame(cmd(&["HDEL", "h", "f"])).unwrap(),
2703 Command::HDel {
2704 key: "h".into(),
2705 fields: vec!["f".into()],
2706 },
2707 );
2708 }
2709
2710 #[test]
2711 fn hdel_multiple() {
2712 let parsed = Command::from_frame(cmd(&["HDEL", "h", "f1", "f2", "f3"])).unwrap();
2713 match parsed {
2714 Command::HDel { fields, .. } => assert_eq!(fields.len(), 3),
2715 other => panic!("expected HDel, got {other:?}"),
2716 }
2717 }
2718
2719 #[test]
2720 fn hdel_wrong_arity() {
2721 let err = Command::from_frame(cmd(&["HDEL", "h"])).unwrap_err();
2722 assert!(matches!(err, ProtocolError::WrongArity(_)));
2723 }
2724
2725 #[test]
2726 fn hexists_basic() {
2727 assert_eq!(
2728 Command::from_frame(cmd(&["HEXISTS", "h", "f"])).unwrap(),
2729 Command::HExists {
2730 key: "h".into(),
2731 field: "f".into(),
2732 },
2733 );
2734 }
2735
2736 #[test]
2737 fn hlen_basic() {
2738 assert_eq!(
2739 Command::from_frame(cmd(&["HLEN", "h"])).unwrap(),
2740 Command::HLen { key: "h".into() },
2741 );
2742 }
2743
2744 #[test]
2745 fn hincrby_basic() {
2746 assert_eq!(
2747 Command::from_frame(cmd(&["HINCRBY", "h", "f", "5"])).unwrap(),
2748 Command::HIncrBy {
2749 key: "h".into(),
2750 field: "f".into(),
2751 delta: 5,
2752 },
2753 );
2754 }
2755
2756 #[test]
2757 fn hincrby_negative() {
2758 assert_eq!(
2759 Command::from_frame(cmd(&["HINCRBY", "h", "f", "-3"])).unwrap(),
2760 Command::HIncrBy {
2761 key: "h".into(),
2762 field: "f".into(),
2763 delta: -3,
2764 },
2765 );
2766 }
2767
2768 #[test]
2769 fn hincrby_wrong_arity() {
2770 let err = Command::from_frame(cmd(&["HINCRBY", "h", "f"])).unwrap_err();
2771 assert!(matches!(err, ProtocolError::WrongArity(_)));
2772 }
2773
2774 #[test]
2775 fn hkeys_basic() {
2776 assert_eq!(
2777 Command::from_frame(cmd(&["HKEYS", "h"])).unwrap(),
2778 Command::HKeys { key: "h".into() },
2779 );
2780 }
2781
2782 #[test]
2783 fn hvals_basic() {
2784 assert_eq!(
2785 Command::from_frame(cmd(&["HVALS", "h"])).unwrap(),
2786 Command::HVals { key: "h".into() },
2787 );
2788 }
2789
2790 #[test]
2791 fn hmget_basic() {
2792 assert_eq!(
2793 Command::from_frame(cmd(&["HMGET", "h", "f1", "f2"])).unwrap(),
2794 Command::HMGet {
2795 key: "h".into(),
2796 fields: vec!["f1".into(), "f2".into()],
2797 },
2798 );
2799 }
2800
2801 #[test]
2802 fn hmget_wrong_arity() {
2803 let err = Command::from_frame(cmd(&["HMGET", "h"])).unwrap_err();
2804 assert!(matches!(err, ProtocolError::WrongArity(_)));
2805 }
2806
2807 #[test]
2808 fn hash_commands_case_insensitive() {
2809 assert!(matches!(
2810 Command::from_frame(cmd(&["hset", "h", "f", "v"])).unwrap(),
2811 Command::HSet { .. }
2812 ));
2813 assert!(matches!(
2814 Command::from_frame(cmd(&["hget", "h", "f"])).unwrap(),
2815 Command::HGet { .. }
2816 ));
2817 assert!(matches!(
2818 Command::from_frame(cmd(&["hgetall", "h"])).unwrap(),
2819 Command::HGetAll { .. }
2820 ));
2821 }
2822
2823 #[test]
2826 fn sadd_single_member() {
2827 assert_eq!(
2828 Command::from_frame(cmd(&["SADD", "s", "member"])).unwrap(),
2829 Command::SAdd {
2830 key: "s".into(),
2831 members: vec!["member".into()],
2832 },
2833 );
2834 }
2835
2836 #[test]
2837 fn sadd_multiple_members() {
2838 let parsed = Command::from_frame(cmd(&["SADD", "s", "a", "b", "c"])).unwrap();
2839 match parsed {
2840 Command::SAdd { key, members } => {
2841 assert_eq!(key, "s");
2842 assert_eq!(members.len(), 3);
2843 }
2844 other => panic!("expected SAdd, got {other:?}"),
2845 }
2846 }
2847
2848 #[test]
2849 fn sadd_wrong_arity() {
2850 let err = Command::from_frame(cmd(&["SADD", "s"])).unwrap_err();
2851 assert!(matches!(err, ProtocolError::WrongArity(_)));
2852 }
2853
2854 #[test]
2855 fn srem_single_member() {
2856 assert_eq!(
2857 Command::from_frame(cmd(&["SREM", "s", "member"])).unwrap(),
2858 Command::SRem {
2859 key: "s".into(),
2860 members: vec!["member".into()],
2861 },
2862 );
2863 }
2864
2865 #[test]
2866 fn srem_multiple_members() {
2867 let parsed = Command::from_frame(cmd(&["SREM", "s", "a", "b"])).unwrap();
2868 match parsed {
2869 Command::SRem { key, members } => {
2870 assert_eq!(key, "s");
2871 assert_eq!(members.len(), 2);
2872 }
2873 other => panic!("expected SRem, got {other:?}"),
2874 }
2875 }
2876
2877 #[test]
2878 fn srem_wrong_arity() {
2879 let err = Command::from_frame(cmd(&["SREM", "s"])).unwrap_err();
2880 assert!(matches!(err, ProtocolError::WrongArity(_)));
2881 }
2882
2883 #[test]
2884 fn smembers_basic() {
2885 assert_eq!(
2886 Command::from_frame(cmd(&["SMEMBERS", "s"])).unwrap(),
2887 Command::SMembers { key: "s".into() },
2888 );
2889 }
2890
2891 #[test]
2892 fn smembers_wrong_arity() {
2893 let err = Command::from_frame(cmd(&["SMEMBERS"])).unwrap_err();
2894 assert!(matches!(err, ProtocolError::WrongArity(_)));
2895 }
2896
2897 #[test]
2898 fn sismember_basic() {
2899 assert_eq!(
2900 Command::from_frame(cmd(&["SISMEMBER", "s", "member"])).unwrap(),
2901 Command::SIsMember {
2902 key: "s".into(),
2903 member: "member".into(),
2904 },
2905 );
2906 }
2907
2908 #[test]
2909 fn sismember_wrong_arity() {
2910 let err = Command::from_frame(cmd(&["SISMEMBER", "s"])).unwrap_err();
2911 assert!(matches!(err, ProtocolError::WrongArity(_)));
2912 }
2913
2914 #[test]
2915 fn scard_basic() {
2916 assert_eq!(
2917 Command::from_frame(cmd(&["SCARD", "s"])).unwrap(),
2918 Command::SCard { key: "s".into() },
2919 );
2920 }
2921
2922 #[test]
2923 fn scard_wrong_arity() {
2924 let err = Command::from_frame(cmd(&["SCARD"])).unwrap_err();
2925 assert!(matches!(err, ProtocolError::WrongArity(_)));
2926 }
2927
2928 #[test]
2929 fn set_commands_case_insensitive() {
2930 assert!(matches!(
2931 Command::from_frame(cmd(&["sadd", "s", "m"])).unwrap(),
2932 Command::SAdd { .. }
2933 ));
2934 assert!(matches!(
2935 Command::from_frame(cmd(&["srem", "s", "m"])).unwrap(),
2936 Command::SRem { .. }
2937 ));
2938 assert!(matches!(
2939 Command::from_frame(cmd(&["smembers", "s"])).unwrap(),
2940 Command::SMembers { .. }
2941 ));
2942 }
2943
2944 #[test]
2947 fn cluster_info_basic() {
2948 assert_eq!(
2949 Command::from_frame(cmd(&["CLUSTER", "INFO"])).unwrap(),
2950 Command::ClusterInfo,
2951 );
2952 }
2953
2954 #[test]
2955 fn cluster_nodes_basic() {
2956 assert_eq!(
2957 Command::from_frame(cmd(&["CLUSTER", "NODES"])).unwrap(),
2958 Command::ClusterNodes,
2959 );
2960 }
2961
2962 #[test]
2963 fn cluster_slots_basic() {
2964 assert_eq!(
2965 Command::from_frame(cmd(&["CLUSTER", "SLOTS"])).unwrap(),
2966 Command::ClusterSlots,
2967 );
2968 }
2969
2970 #[test]
2971 fn cluster_keyslot_basic() {
2972 assert_eq!(
2973 Command::from_frame(cmd(&["CLUSTER", "KEYSLOT", "mykey"])).unwrap(),
2974 Command::ClusterKeySlot {
2975 key: "mykey".into()
2976 },
2977 );
2978 }
2979
2980 #[test]
2981 fn cluster_keyslot_wrong_arity() {
2982 let err = Command::from_frame(cmd(&["CLUSTER", "KEYSLOT"])).unwrap_err();
2983 assert!(matches!(err, ProtocolError::WrongArity(_)));
2984 }
2985
2986 #[test]
2987 fn cluster_myid_basic() {
2988 assert_eq!(
2989 Command::from_frame(cmd(&["CLUSTER", "MYID"])).unwrap(),
2990 Command::ClusterMyId,
2991 );
2992 }
2993
2994 #[test]
2995 fn cluster_unknown_subcommand() {
2996 let err = Command::from_frame(cmd(&["CLUSTER", "BADCMD"])).unwrap_err();
2997 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2998 }
2999
3000 #[test]
3001 fn cluster_no_subcommand() {
3002 let err = Command::from_frame(cmd(&["CLUSTER"])).unwrap_err();
3003 assert!(matches!(err, ProtocolError::WrongArity(_)));
3004 }
3005
3006 #[test]
3007 fn cluster_case_insensitive() {
3008 assert!(matches!(
3009 Command::from_frame(cmd(&["cluster", "info"])).unwrap(),
3010 Command::ClusterInfo
3011 ));
3012 assert!(matches!(
3013 Command::from_frame(cmd(&["cluster", "keyslot", "k"])).unwrap(),
3014 Command::ClusterKeySlot { .. }
3015 ));
3016 }
3017
3018 #[test]
3019 fn asking_basic() {
3020 assert_eq!(
3021 Command::from_frame(cmd(&["ASKING"])).unwrap(),
3022 Command::Asking,
3023 );
3024 }
3025
3026 #[test]
3027 fn asking_wrong_arity() {
3028 let err = Command::from_frame(cmd(&["ASKING", "extra"])).unwrap_err();
3029 assert!(matches!(err, ProtocolError::WrongArity(_)));
3030 }
3031
3032 #[test]
3033 fn cluster_setslot_importing() {
3034 assert_eq!(
3035 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "100", "IMPORTING", "node123"]))
3036 .unwrap(),
3037 Command::ClusterSetSlotImporting {
3038 slot: 100,
3039 node_id: "node123".into()
3040 },
3041 );
3042 }
3043
3044 #[test]
3045 fn cluster_setslot_migrating() {
3046 assert_eq!(
3047 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "200", "MIGRATING", "node456"]))
3048 .unwrap(),
3049 Command::ClusterSetSlotMigrating {
3050 slot: 200,
3051 node_id: "node456".into()
3052 },
3053 );
3054 }
3055
3056 #[test]
3057 fn cluster_setslot_node() {
3058 assert_eq!(
3059 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "300", "NODE", "node789"])).unwrap(),
3060 Command::ClusterSetSlotNode {
3061 slot: 300,
3062 node_id: "node789".into()
3063 },
3064 );
3065 }
3066
3067 #[test]
3068 fn cluster_setslot_stable() {
3069 assert_eq!(
3070 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "400", "STABLE"])).unwrap(),
3071 Command::ClusterSetSlotStable { slot: 400 },
3072 );
3073 }
3074
3075 #[test]
3076 fn cluster_setslot_invalid_slot() {
3077 let err =
3078 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "notanumber", "STABLE"])).unwrap_err();
3079 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3080 }
3081
3082 #[test]
3083 fn cluster_setslot_wrong_arity() {
3084 let err = Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "100"])).unwrap_err();
3085 assert!(matches!(err, ProtocolError::WrongArity(_)));
3086 }
3087
3088 #[test]
3089 fn migrate_basic() {
3090 assert_eq!(
3091 Command::from_frame(cmd(&["MIGRATE", "127.0.0.1", "6379", "mykey", "0", "5000"]))
3092 .unwrap(),
3093 Command::Migrate {
3094 host: "127.0.0.1".into(),
3095 port: 6379,
3096 key: "mykey".into(),
3097 db: 0,
3098 timeout_ms: 5000,
3099 copy: false,
3100 replace: false,
3101 },
3102 );
3103 }
3104
3105 #[test]
3106 fn migrate_with_options() {
3107 assert_eq!(
3108 Command::from_frame(cmd(&[
3109 "MIGRATE",
3110 "192.168.1.1",
3111 "6380",
3112 "testkey",
3113 "1",
3114 "10000",
3115 "COPY",
3116 "REPLACE"
3117 ]))
3118 .unwrap(),
3119 Command::Migrate {
3120 host: "192.168.1.1".into(),
3121 port: 6380,
3122 key: "testkey".into(),
3123 db: 1,
3124 timeout_ms: 10000,
3125 copy: true,
3126 replace: true,
3127 },
3128 );
3129 }
3130
3131 #[test]
3132 fn migrate_wrong_arity() {
3133 let err = Command::from_frame(cmd(&["MIGRATE", "host", "port", "key"])).unwrap_err();
3134 assert!(matches!(err, ProtocolError::WrongArity(_)));
3135 }
3136
3137 #[test]
3138 fn migrate_invalid_port() {
3139 let err = Command::from_frame(cmd(&["MIGRATE", "host", "notaport", "key", "0", "1000"]))
3140 .unwrap_err();
3141 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3142 }
3143
3144 #[test]
3145 fn cluster_meet_basic() {
3146 assert_eq!(
3147 Command::from_frame(cmd(&["CLUSTER", "MEET", "192.168.1.1", "6379"])).unwrap(),
3148 Command::ClusterMeet {
3149 ip: "192.168.1.1".into(),
3150 port: 6379
3151 },
3152 );
3153 }
3154
3155 #[test]
3156 fn cluster_addslots_basic() {
3157 assert_eq!(
3158 Command::from_frame(cmd(&["CLUSTER", "ADDSLOTS", "0", "1", "2"])).unwrap(),
3159 Command::ClusterAddSlots {
3160 slots: vec![0, 1, 2]
3161 },
3162 );
3163 }
3164
3165 #[test]
3166 fn cluster_delslots_basic() {
3167 assert_eq!(
3168 Command::from_frame(cmd(&["CLUSTER", "DELSLOTS", "100", "101"])).unwrap(),
3169 Command::ClusterDelSlots {
3170 slots: vec![100, 101]
3171 },
3172 );
3173 }
3174
3175 #[test]
3176 fn cluster_forget_basic() {
3177 assert_eq!(
3178 Command::from_frame(cmd(&["CLUSTER", "FORGET", "abc123"])).unwrap(),
3179 Command::ClusterForget {
3180 node_id: "abc123".into()
3181 },
3182 );
3183 }
3184
3185 #[test]
3186 fn cluster_replicate_basic() {
3187 assert_eq!(
3188 Command::from_frame(cmd(&["CLUSTER", "REPLICATE", "master-id"])).unwrap(),
3189 Command::ClusterReplicate {
3190 node_id: "master-id".into()
3191 },
3192 );
3193 }
3194
3195 #[test]
3196 fn cluster_failover_basic() {
3197 assert_eq!(
3198 Command::from_frame(cmd(&["CLUSTER", "FAILOVER"])).unwrap(),
3199 Command::ClusterFailover {
3200 force: false,
3201 takeover: false
3202 },
3203 );
3204 }
3205
3206 #[test]
3207 fn cluster_failover_force() {
3208 assert_eq!(
3209 Command::from_frame(cmd(&["CLUSTER", "FAILOVER", "FORCE"])).unwrap(),
3210 Command::ClusterFailover {
3211 force: true,
3212 takeover: false
3213 },
3214 );
3215 }
3216
3217 #[test]
3218 fn cluster_failover_takeover() {
3219 assert_eq!(
3220 Command::from_frame(cmd(&["CLUSTER", "FAILOVER", "TAKEOVER"])).unwrap(),
3221 Command::ClusterFailover {
3222 force: false,
3223 takeover: true
3224 },
3225 );
3226 }
3227
3228 #[test]
3229 fn cluster_countkeysinslot_basic() {
3230 assert_eq!(
3231 Command::from_frame(cmd(&["CLUSTER", "COUNTKEYSINSLOT", "100"])).unwrap(),
3232 Command::ClusterCountKeysInSlot { slot: 100 },
3233 );
3234 }
3235
3236 #[test]
3237 fn cluster_getkeysinslot_basic() {
3238 assert_eq!(
3239 Command::from_frame(cmd(&["CLUSTER", "GETKEYSINSLOT", "200", "10"])).unwrap(),
3240 Command::ClusterGetKeysInSlot {
3241 slot: 200,
3242 count: 10
3243 },
3244 );
3245 }
3246}