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