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 IncrBy { key: String, delta: i64 },
52
53 DecrBy { key: String, delta: i64 },
55
56 IncrByFloat { key: String, delta: f64 },
58
59 Append { key: String, value: Bytes },
61
62 Strlen { key: String },
64
65 Keys { pattern: String },
67
68 Rename { key: String, newkey: String },
70
71 Del { keys: Vec<String> },
73
74 Exists { keys: Vec<String> },
76
77 MGet { keys: Vec<String> },
79
80 MSet { pairs: Vec<(String, Bytes)> },
82
83 Expire { key: String, seconds: u64 },
85
86 Ttl { key: String },
88
89 Persist { key: String },
91
92 Pttl { key: String },
94
95 Pexpire { key: String, milliseconds: u64 },
97
98 DbSize,
100
101 Info { section: Option<String> },
103
104 BgSave,
106
107 BgRewriteAof,
109
110 FlushDb,
112
113 Scan {
115 cursor: u64,
116 pattern: Option<String>,
117 count: Option<usize>,
118 },
119
120 LPush { key: String, values: Vec<Bytes> },
122
123 RPush { key: String, values: Vec<Bytes> },
125
126 LPop { key: String },
128
129 RPop { key: String },
131
132 LRange { key: String, start: i64, stop: i64 },
134
135 LLen { key: String },
137
138 Type { key: String },
140
141 ZAdd {
143 key: String,
144 flags: ZAddFlags,
145 members: Vec<(f64, String)>,
146 },
147
148 ZRem { key: String, members: Vec<String> },
150
151 ZScore { key: String, member: String },
153
154 ZRank { key: String, member: String },
156
157 ZCard { key: String },
159
160 ZRange {
162 key: String,
163 start: i64,
164 stop: i64,
165 with_scores: bool,
166 },
167
168 HSet {
170 key: String,
171 fields: Vec<(String, Bytes)>,
172 },
173
174 HGet { key: String, field: String },
176
177 HGetAll { key: String },
179
180 HDel { key: String, fields: Vec<String> },
182
183 HExists { key: String, field: String },
185
186 HLen { key: String },
188
189 HIncrBy {
191 key: String,
192 field: String,
193 delta: i64,
194 },
195
196 HKeys { key: String },
198
199 HVals { key: String },
201
202 HMGet { key: String, fields: Vec<String> },
204
205 SAdd { key: String, members: Vec<String> },
207
208 SRem { key: String, members: Vec<String> },
210
211 SMembers { key: String },
213
214 SIsMember { key: String, member: String },
216
217 SCard { key: String },
219
220 ClusterInfo,
223
224 ClusterNodes,
226
227 ClusterSlots,
229
230 ClusterKeySlot { key: String },
232
233 ClusterMyId,
235
236 ClusterSetSlotImporting { slot: u16, node_id: String },
238
239 ClusterSetSlotMigrating { slot: u16, node_id: String },
241
242 ClusterSetSlotNode { slot: u16, node_id: String },
244
245 ClusterSetSlotStable { slot: u16 },
247
248 ClusterMeet { ip: String, port: u16 },
250
251 ClusterAddSlots { slots: Vec<u16> },
253
254 ClusterDelSlots { slots: Vec<u16> },
256
257 ClusterForget { node_id: String },
259
260 ClusterReplicate { node_id: String },
262
263 ClusterFailover { force: bool, takeover: bool },
265
266 ClusterCountKeysInSlot { slot: u16 },
268
269 ClusterGetKeysInSlot { slot: u16, count: u32 },
271
272 Migrate {
275 host: String,
276 port: u16,
277 key: String,
278 db: u32,
279 timeout_ms: u64,
280 copy: bool,
281 replace: bool,
282 },
283
284 Asking,
286
287 SlowLogGet { count: Option<usize> },
289
290 SlowLogLen,
292
293 SlowLogReset,
295
296 Subscribe { channels: Vec<String> },
299
300 Unsubscribe { channels: Vec<String> },
302
303 PSubscribe { patterns: Vec<String> },
305
306 PUnsubscribe { patterns: Vec<String> },
308
309 Publish { channel: String, message: Bytes },
311
312 PubSubChannels { pattern: Option<String> },
314
315 PubSubNumSub { channels: Vec<String> },
317
318 PubSubNumPat,
320
321 Auth {
323 username: Option<String>,
325 password: String,
327 },
328
329 Quit,
331
332 Unknown(String),
334}
335
336#[derive(Debug, Clone, Default, PartialEq)]
338pub struct ZAddFlags {
339 pub nx: bool,
341 pub xx: bool,
343 pub gt: bool,
345 pub lt: bool,
347 pub ch: bool,
349}
350
351impl Eq for ZAddFlags {}
352
353impl Command {
354 pub fn command_name(&self) -> &'static str {
359 match self {
360 Command::Ping(_) => "ping",
361 Command::Echo(_) => "echo",
362 Command::Get { .. } => "get",
363 Command::Set { .. } => "set",
364 Command::Incr { .. } => "incr",
365 Command::Decr { .. } => "decr",
366 Command::IncrBy { .. } => "incrby",
367 Command::DecrBy { .. } => "decrby",
368 Command::IncrByFloat { .. } => "incrbyfloat",
369 Command::Append { .. } => "append",
370 Command::Strlen { .. } => "strlen",
371 Command::Keys { .. } => "keys",
372 Command::Rename { .. } => "rename",
373 Command::Del { .. } => "del",
374 Command::Exists { .. } => "exists",
375 Command::MGet { .. } => "mget",
376 Command::MSet { .. } => "mset",
377 Command::Expire { .. } => "expire",
378 Command::Ttl { .. } => "ttl",
379 Command::Persist { .. } => "persist",
380 Command::Pttl { .. } => "pttl",
381 Command::Pexpire { .. } => "pexpire",
382 Command::DbSize => "dbsize",
383 Command::Info { .. } => "info",
384 Command::BgSave => "bgsave",
385 Command::BgRewriteAof => "bgrewriteaof",
386 Command::FlushDb => "flushdb",
387 Command::Scan { .. } => "scan",
388 Command::LPush { .. } => "lpush",
389 Command::RPush { .. } => "rpush",
390 Command::LPop { .. } => "lpop",
391 Command::RPop { .. } => "rpop",
392 Command::LRange { .. } => "lrange",
393 Command::LLen { .. } => "llen",
394 Command::Type { .. } => "type",
395 Command::ZAdd { .. } => "zadd",
396 Command::ZRem { .. } => "zrem",
397 Command::ZScore { .. } => "zscore",
398 Command::ZRank { .. } => "zrank",
399 Command::ZCard { .. } => "zcard",
400 Command::ZRange { .. } => "zrange",
401 Command::HSet { .. } => "hset",
402 Command::HGet { .. } => "hget",
403 Command::HGetAll { .. } => "hgetall",
404 Command::HDel { .. } => "hdel",
405 Command::HExists { .. } => "hexists",
406 Command::HLen { .. } => "hlen",
407 Command::HIncrBy { .. } => "hincrby",
408 Command::HKeys { .. } => "hkeys",
409 Command::HVals { .. } => "hvals",
410 Command::HMGet { .. } => "hmget",
411 Command::SAdd { .. } => "sadd",
412 Command::SRem { .. } => "srem",
413 Command::SMembers { .. } => "smembers",
414 Command::SIsMember { .. } => "sismember",
415 Command::SCard { .. } => "scard",
416 Command::ClusterInfo => "cluster_info",
417 Command::ClusterNodes => "cluster_nodes",
418 Command::ClusterSlots => "cluster_slots",
419 Command::ClusterKeySlot { .. } => "cluster_keyslot",
420 Command::ClusterMyId => "cluster_myid",
421 Command::ClusterSetSlotImporting { .. } => "cluster_setslot",
422 Command::ClusterSetSlotMigrating { .. } => "cluster_setslot",
423 Command::ClusterSetSlotNode { .. } => "cluster_setslot",
424 Command::ClusterSetSlotStable { .. } => "cluster_setslot",
425 Command::ClusterMeet { .. } => "cluster_meet",
426 Command::ClusterAddSlots { .. } => "cluster_addslots",
427 Command::ClusterDelSlots { .. } => "cluster_delslots",
428 Command::ClusterForget { .. } => "cluster_forget",
429 Command::ClusterReplicate { .. } => "cluster_replicate",
430 Command::ClusterFailover { .. } => "cluster_failover",
431 Command::ClusterCountKeysInSlot { .. } => "cluster_countkeysinslot",
432 Command::ClusterGetKeysInSlot { .. } => "cluster_getkeysinslot",
433 Command::Migrate { .. } => "migrate",
434 Command::Asking => "asking",
435 Command::SlowLogGet { .. } => "slowlog",
436 Command::SlowLogLen => "slowlog",
437 Command::SlowLogReset => "slowlog",
438 Command::Subscribe { .. } => "subscribe",
439 Command::Unsubscribe { .. } => "unsubscribe",
440 Command::PSubscribe { .. } => "psubscribe",
441 Command::PUnsubscribe { .. } => "punsubscribe",
442 Command::Publish { .. } => "publish",
443 Command::PubSubChannels { .. } => "pubsub",
444 Command::PubSubNumSub { .. } => "pubsub",
445 Command::PubSubNumPat => "pubsub",
446 Command::Auth { .. } => "auth",
447 Command::Quit => "quit",
448 Command::Unknown(_) => "unknown",
449 }
450 }
451
452 pub fn from_frame(frame: Frame) -> Result<Command, ProtocolError> {
457 let frames = match frame {
458 Frame::Array(frames) => frames,
459 _ => {
460 return Err(ProtocolError::InvalidCommandFrame(
461 "expected array frame".into(),
462 ));
463 }
464 };
465
466 if frames.is_empty() {
467 return Err(ProtocolError::InvalidCommandFrame(
468 "empty command array".into(),
469 ));
470 }
471
472 let name = extract_string(&frames[0])?;
473 let name_upper = name.to_ascii_uppercase();
474
475 match name_upper.as_str() {
476 "PING" => parse_ping(&frames[1..]),
477 "ECHO" => parse_echo(&frames[1..]),
478 "GET" => parse_get(&frames[1..]),
479 "SET" => parse_set(&frames[1..]),
480 "INCR" => parse_incr(&frames[1..]),
481 "DECR" => parse_decr(&frames[1..]),
482 "INCRBY" => parse_incrby(&frames[1..]),
483 "DECRBY" => parse_decrby(&frames[1..]),
484 "INCRBYFLOAT" => parse_incrbyfloat(&frames[1..]),
485 "APPEND" => parse_append(&frames[1..]),
486 "STRLEN" => parse_strlen(&frames[1..]),
487 "KEYS" => parse_keys(&frames[1..]),
488 "RENAME" => parse_rename(&frames[1..]),
489 "DEL" => parse_del(&frames[1..]),
490 "EXISTS" => parse_exists(&frames[1..]),
491 "MGET" => parse_mget(&frames[1..]),
492 "MSET" => parse_mset(&frames[1..]),
493 "EXPIRE" => parse_expire(&frames[1..]),
494 "TTL" => parse_ttl(&frames[1..]),
495 "PERSIST" => parse_persist(&frames[1..]),
496 "PTTL" => parse_pttl(&frames[1..]),
497 "PEXPIRE" => parse_pexpire(&frames[1..]),
498 "DBSIZE" => parse_dbsize(&frames[1..]),
499 "INFO" => parse_info(&frames[1..]),
500 "BGSAVE" => parse_bgsave(&frames[1..]),
501 "BGREWRITEAOF" => parse_bgrewriteaof(&frames[1..]),
502 "FLUSHDB" => parse_flushdb(&frames[1..]),
503 "SCAN" => parse_scan(&frames[1..]),
504 "LPUSH" => parse_lpush(&frames[1..]),
505 "RPUSH" => parse_rpush(&frames[1..]),
506 "LPOP" => parse_lpop(&frames[1..]),
507 "RPOP" => parse_rpop(&frames[1..]),
508 "LRANGE" => parse_lrange(&frames[1..]),
509 "LLEN" => parse_llen(&frames[1..]),
510 "TYPE" => parse_type(&frames[1..]),
511 "ZADD" => parse_zadd(&frames[1..]),
512 "ZREM" => parse_zrem(&frames[1..]),
513 "ZSCORE" => parse_zscore(&frames[1..]),
514 "ZRANK" => parse_zrank(&frames[1..]),
515 "ZCARD" => parse_zcard(&frames[1..]),
516 "ZRANGE" => parse_zrange(&frames[1..]),
517 "HSET" => parse_hset(&frames[1..]),
518 "HGET" => parse_hget(&frames[1..]),
519 "HGETALL" => parse_hgetall(&frames[1..]),
520 "HDEL" => parse_hdel(&frames[1..]),
521 "HEXISTS" => parse_hexists(&frames[1..]),
522 "HLEN" => parse_hlen(&frames[1..]),
523 "HINCRBY" => parse_hincrby(&frames[1..]),
524 "HKEYS" => parse_hkeys(&frames[1..]),
525 "HVALS" => parse_hvals(&frames[1..]),
526 "HMGET" => parse_hmget(&frames[1..]),
527 "SADD" => parse_sadd(&frames[1..]),
528 "SREM" => parse_srem(&frames[1..]),
529 "SMEMBERS" => parse_smembers(&frames[1..]),
530 "SISMEMBER" => parse_sismember(&frames[1..]),
531 "SCARD" => parse_scard(&frames[1..]),
532 "CLUSTER" => parse_cluster(&frames[1..]),
533 "ASKING" => parse_asking(&frames[1..]),
534 "MIGRATE" => parse_migrate(&frames[1..]),
535 "SLOWLOG" => parse_slowlog(&frames[1..]),
536 "SUBSCRIBE" => parse_subscribe(&frames[1..]),
537 "UNSUBSCRIBE" => parse_unsubscribe(&frames[1..]),
538 "PSUBSCRIBE" => parse_psubscribe(&frames[1..]),
539 "PUNSUBSCRIBE" => parse_punsubscribe(&frames[1..]),
540 "PUBLISH" => parse_publish(&frames[1..]),
541 "PUBSUB" => parse_pubsub(&frames[1..]),
542 "AUTH" => parse_auth(&frames[1..]),
543 "QUIT" => parse_quit(&frames[1..]),
544 _ => Ok(Command::Unknown(name)),
545 }
546 }
547}
548
549fn extract_string(frame: &Frame) -> Result<String, ProtocolError> {
551 match frame {
552 Frame::Bulk(data) => String::from_utf8(data.to_vec()).map_err(|_| {
553 ProtocolError::InvalidCommandFrame("command name is not valid utf-8".into())
554 }),
555 Frame::Simple(s) => Ok(s.clone()),
556 _ => Err(ProtocolError::InvalidCommandFrame(
557 "expected bulk or simple string for command name".into(),
558 )),
559 }
560}
561
562fn extract_bytes(frame: &Frame) -> Result<Bytes, ProtocolError> {
564 match frame {
565 Frame::Bulk(data) => Ok(data.clone()),
566 Frame::Simple(s) => Ok(Bytes::from(s.clone().into_bytes())),
567 _ => Err(ProtocolError::InvalidCommandFrame(
568 "expected bulk or simple string argument".into(),
569 )),
570 }
571}
572
573fn parse_u64(frame: &Frame, cmd: &str) -> Result<u64, ProtocolError> {
575 let s = extract_string(frame)?;
576 s.parse::<u64>().map_err(|_| {
577 ProtocolError::InvalidCommandFrame(format!("value is not a valid integer for '{cmd}'"))
578 })
579}
580
581fn parse_ping(args: &[Frame]) -> Result<Command, ProtocolError> {
582 match args.len() {
583 0 => Ok(Command::Ping(None)),
584 1 => {
585 let msg = extract_bytes(&args[0])?;
586 Ok(Command::Ping(Some(msg)))
587 }
588 _ => Err(ProtocolError::WrongArity("PING".into())),
589 }
590}
591
592fn parse_echo(args: &[Frame]) -> Result<Command, ProtocolError> {
593 if args.len() != 1 {
594 return Err(ProtocolError::WrongArity("ECHO".into()));
595 }
596 let msg = extract_bytes(&args[0])?;
597 Ok(Command::Echo(msg))
598}
599
600fn parse_get(args: &[Frame]) -> Result<Command, ProtocolError> {
601 if args.len() != 1 {
602 return Err(ProtocolError::WrongArity("GET".into()));
603 }
604 let key = extract_string(&args[0])?;
605 Ok(Command::Get { key })
606}
607
608fn parse_set(args: &[Frame]) -> Result<Command, ProtocolError> {
609 if args.len() < 2 {
610 return Err(ProtocolError::WrongArity("SET".into()));
611 }
612
613 let key = extract_string(&args[0])?;
614 let value = extract_bytes(&args[1])?;
615
616 let mut expire = None;
617 let mut nx = false;
618 let mut xx = false;
619 let mut idx = 2;
620
621 while idx < args.len() {
622 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
623 match flag.as_str() {
624 "NX" => {
625 nx = true;
626 idx += 1;
627 }
628 "XX" => {
629 xx = true;
630 idx += 1;
631 }
632 "EX" => {
633 idx += 1;
634 if idx >= args.len() {
635 return Err(ProtocolError::WrongArity("SET".into()));
636 }
637 let amount = parse_u64(&args[idx], "SET")?;
638 if amount == 0 {
639 return Err(ProtocolError::InvalidCommandFrame(
640 "invalid expire time in 'SET' command".into(),
641 ));
642 }
643 expire = Some(SetExpire::Ex(amount));
644 idx += 1;
645 }
646 "PX" => {
647 idx += 1;
648 if idx >= args.len() {
649 return Err(ProtocolError::WrongArity("SET".into()));
650 }
651 let amount = parse_u64(&args[idx], "SET")?;
652 if amount == 0 {
653 return Err(ProtocolError::InvalidCommandFrame(
654 "invalid expire time in 'SET' command".into(),
655 ));
656 }
657 expire = Some(SetExpire::Px(amount));
658 idx += 1;
659 }
660 _ => {
661 return Err(ProtocolError::InvalidCommandFrame(format!(
662 "unsupported SET option '{flag}'"
663 )));
664 }
665 }
666 }
667
668 if nx && xx {
669 return Err(ProtocolError::InvalidCommandFrame(
670 "XX and NX options at the same time are not compatible".into(),
671 ));
672 }
673
674 Ok(Command::Set {
675 key,
676 value,
677 expire,
678 nx,
679 xx,
680 })
681}
682
683fn parse_incr(args: &[Frame]) -> Result<Command, ProtocolError> {
684 if args.len() != 1 {
685 return Err(ProtocolError::WrongArity("INCR".into()));
686 }
687 let key = extract_string(&args[0])?;
688 Ok(Command::Incr { key })
689}
690
691fn parse_decr(args: &[Frame]) -> Result<Command, ProtocolError> {
692 if args.len() != 1 {
693 return Err(ProtocolError::WrongArity("DECR".into()));
694 }
695 let key = extract_string(&args[0])?;
696 Ok(Command::Decr { key })
697}
698
699fn parse_incrby(args: &[Frame]) -> Result<Command, ProtocolError> {
700 if args.len() != 2 {
701 return Err(ProtocolError::WrongArity("INCRBY".into()));
702 }
703 let key = extract_string(&args[0])?;
704 let delta = parse_i64(&args[1], "INCRBY")?;
705 Ok(Command::IncrBy { key, delta })
706}
707
708fn parse_decrby(args: &[Frame]) -> Result<Command, ProtocolError> {
709 if args.len() != 2 {
710 return Err(ProtocolError::WrongArity("DECRBY".into()));
711 }
712 let key = extract_string(&args[0])?;
713 let delta = parse_i64(&args[1], "DECRBY")?;
714 Ok(Command::DecrBy { key, delta })
715}
716
717fn parse_incrbyfloat(args: &[Frame]) -> Result<Command, ProtocolError> {
718 if args.len() != 2 {
719 return Err(ProtocolError::WrongArity("INCRBYFLOAT".into()));
720 }
721 let key = extract_string(&args[0])?;
722 let s = extract_string(&args[1])?;
723 let delta: f64 = s.parse().map_err(|_| {
724 ProtocolError::InvalidCommandFrame("value is not a valid float for 'INCRBYFLOAT'".into())
725 })?;
726 if delta.is_nan() || delta.is_infinite() {
727 return Err(ProtocolError::InvalidCommandFrame(
728 "increment would produce NaN or Infinity".into(),
729 ));
730 }
731 Ok(Command::IncrByFloat { key, delta })
732}
733
734fn parse_append(args: &[Frame]) -> Result<Command, ProtocolError> {
735 if args.len() != 2 {
736 return Err(ProtocolError::WrongArity("APPEND".into()));
737 }
738 let key = extract_string(&args[0])?;
739 let value = extract_bytes(&args[1])?;
740 Ok(Command::Append { key, value })
741}
742
743fn parse_strlen(args: &[Frame]) -> Result<Command, ProtocolError> {
744 if args.len() != 1 {
745 return Err(ProtocolError::WrongArity("STRLEN".into()));
746 }
747 let key = extract_string(&args[0])?;
748 Ok(Command::Strlen { key })
749}
750
751fn parse_keys(args: &[Frame]) -> Result<Command, ProtocolError> {
752 if args.len() != 1 {
753 return Err(ProtocolError::WrongArity("KEYS".into()));
754 }
755 let pattern = extract_string(&args[0])?;
756 Ok(Command::Keys { pattern })
757}
758
759fn parse_rename(args: &[Frame]) -> Result<Command, ProtocolError> {
760 if args.len() != 2 {
761 return Err(ProtocolError::WrongArity("RENAME".into()));
762 }
763 let key = extract_string(&args[0])?;
764 let newkey = extract_string(&args[1])?;
765 Ok(Command::Rename { key, newkey })
766}
767
768fn parse_del(args: &[Frame]) -> Result<Command, ProtocolError> {
769 if args.is_empty() {
770 return Err(ProtocolError::WrongArity("DEL".into()));
771 }
772 let keys = args
773 .iter()
774 .map(extract_string)
775 .collect::<Result<Vec<_>, _>>()?;
776 Ok(Command::Del { keys })
777}
778
779fn parse_exists(args: &[Frame]) -> Result<Command, ProtocolError> {
780 if args.is_empty() {
781 return Err(ProtocolError::WrongArity("EXISTS".into()));
782 }
783 let keys = args
784 .iter()
785 .map(extract_string)
786 .collect::<Result<Vec<_>, _>>()?;
787 Ok(Command::Exists { keys })
788}
789
790fn parse_mget(args: &[Frame]) -> Result<Command, ProtocolError> {
791 if args.is_empty() {
792 return Err(ProtocolError::WrongArity("MGET".into()));
793 }
794 let keys = args
795 .iter()
796 .map(extract_string)
797 .collect::<Result<Vec<_>, _>>()?;
798 Ok(Command::MGet { keys })
799}
800
801fn parse_mset(args: &[Frame]) -> Result<Command, ProtocolError> {
802 if args.is_empty() || !args.len().is_multiple_of(2) {
803 return Err(ProtocolError::WrongArity("MSET".into()));
804 }
805 let mut pairs = Vec::with_capacity(args.len() / 2);
806 for chunk in args.chunks(2) {
807 let key = extract_string(&chunk[0])?;
808 let value = extract_bytes(&chunk[1])?;
809 pairs.push((key, value));
810 }
811 Ok(Command::MSet { pairs })
812}
813
814fn parse_expire(args: &[Frame]) -> Result<Command, ProtocolError> {
815 if args.len() != 2 {
816 return Err(ProtocolError::WrongArity("EXPIRE".into()));
817 }
818 let key = extract_string(&args[0])?;
819 let seconds = parse_u64(&args[1], "EXPIRE")?;
820
821 if seconds == 0 {
822 return Err(ProtocolError::InvalidCommandFrame(
823 "invalid expire time in 'EXPIRE' command".into(),
824 ));
825 }
826
827 Ok(Command::Expire { key, seconds })
828}
829
830fn parse_ttl(args: &[Frame]) -> Result<Command, ProtocolError> {
831 if args.len() != 1 {
832 return Err(ProtocolError::WrongArity("TTL".into()));
833 }
834 let key = extract_string(&args[0])?;
835 Ok(Command::Ttl { key })
836}
837
838fn parse_persist(args: &[Frame]) -> Result<Command, ProtocolError> {
839 if args.len() != 1 {
840 return Err(ProtocolError::WrongArity("PERSIST".into()));
841 }
842 let key = extract_string(&args[0])?;
843 Ok(Command::Persist { key })
844}
845
846fn parse_pttl(args: &[Frame]) -> Result<Command, ProtocolError> {
847 if args.len() != 1 {
848 return Err(ProtocolError::WrongArity("PTTL".into()));
849 }
850 let key = extract_string(&args[0])?;
851 Ok(Command::Pttl { key })
852}
853
854fn parse_pexpire(args: &[Frame]) -> Result<Command, ProtocolError> {
855 if args.len() != 2 {
856 return Err(ProtocolError::WrongArity("PEXPIRE".into()));
857 }
858 let key = extract_string(&args[0])?;
859 let milliseconds = parse_u64(&args[1], "PEXPIRE")?;
860
861 if milliseconds == 0 {
862 return Err(ProtocolError::InvalidCommandFrame(
863 "invalid expire time in 'PEXPIRE' command".into(),
864 ));
865 }
866
867 Ok(Command::Pexpire { key, milliseconds })
868}
869
870fn parse_dbsize(args: &[Frame]) -> Result<Command, ProtocolError> {
871 if !args.is_empty() {
872 return Err(ProtocolError::WrongArity("DBSIZE".into()));
873 }
874 Ok(Command::DbSize)
875}
876
877fn parse_info(args: &[Frame]) -> Result<Command, ProtocolError> {
878 match args.len() {
879 0 => Ok(Command::Info { section: None }),
880 1 => {
881 let section = extract_string(&args[0])?;
882 Ok(Command::Info {
883 section: Some(section),
884 })
885 }
886 _ => Err(ProtocolError::WrongArity("INFO".into())),
887 }
888}
889
890fn parse_bgsave(args: &[Frame]) -> Result<Command, ProtocolError> {
891 if !args.is_empty() {
892 return Err(ProtocolError::WrongArity("BGSAVE".into()));
893 }
894 Ok(Command::BgSave)
895}
896
897fn parse_bgrewriteaof(args: &[Frame]) -> Result<Command, ProtocolError> {
898 if !args.is_empty() {
899 return Err(ProtocolError::WrongArity("BGREWRITEAOF".into()));
900 }
901 Ok(Command::BgRewriteAof)
902}
903
904fn parse_flushdb(args: &[Frame]) -> Result<Command, ProtocolError> {
905 if !args.is_empty() {
906 return Err(ProtocolError::WrongArity("FLUSHDB".into()));
907 }
908 Ok(Command::FlushDb)
909}
910
911fn parse_scan(args: &[Frame]) -> Result<Command, ProtocolError> {
912 if args.is_empty() {
913 return Err(ProtocolError::WrongArity("SCAN".into()));
914 }
915
916 let cursor = parse_u64(&args[0], "SCAN")?;
917 let mut pattern = None;
918 let mut count = None;
919 let mut idx = 1;
920
921 while idx < args.len() {
922 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
923 match flag.as_str() {
924 "MATCH" => {
925 idx += 1;
926 if idx >= args.len() {
927 return Err(ProtocolError::WrongArity("SCAN".into()));
928 }
929 pattern = Some(extract_string(&args[idx])?);
930 idx += 1;
931 }
932 "COUNT" => {
933 idx += 1;
934 if idx >= args.len() {
935 return Err(ProtocolError::WrongArity("SCAN".into()));
936 }
937 let n = parse_u64(&args[idx], "SCAN")?;
938 count = Some(n as usize);
939 idx += 1;
940 }
941 _ => {
942 return Err(ProtocolError::InvalidCommandFrame(format!(
943 "unsupported SCAN option '{flag}'"
944 )));
945 }
946 }
947 }
948
949 Ok(Command::Scan {
950 cursor,
951 pattern,
952 count,
953 })
954}
955
956fn parse_i64(frame: &Frame, cmd: &str) -> Result<i64, ProtocolError> {
958 let s = extract_string(frame)?;
959 s.parse::<i64>().map_err(|_| {
960 ProtocolError::InvalidCommandFrame(format!("value is not a valid integer for '{cmd}'"))
961 })
962}
963
964fn parse_lpush(args: &[Frame]) -> Result<Command, ProtocolError> {
965 if args.len() < 2 {
966 return Err(ProtocolError::WrongArity("LPUSH".into()));
967 }
968 let key = extract_string(&args[0])?;
969 let values = args[1..]
970 .iter()
971 .map(extract_bytes)
972 .collect::<Result<Vec<_>, _>>()?;
973 Ok(Command::LPush { key, values })
974}
975
976fn parse_rpush(args: &[Frame]) -> Result<Command, ProtocolError> {
977 if args.len() < 2 {
978 return Err(ProtocolError::WrongArity("RPUSH".into()));
979 }
980 let key = extract_string(&args[0])?;
981 let values = args[1..]
982 .iter()
983 .map(extract_bytes)
984 .collect::<Result<Vec<_>, _>>()?;
985 Ok(Command::RPush { key, values })
986}
987
988fn parse_lpop(args: &[Frame]) -> Result<Command, ProtocolError> {
989 if args.len() != 1 {
990 return Err(ProtocolError::WrongArity("LPOP".into()));
991 }
992 let key = extract_string(&args[0])?;
993 Ok(Command::LPop { key })
994}
995
996fn parse_rpop(args: &[Frame]) -> Result<Command, ProtocolError> {
997 if args.len() != 1 {
998 return Err(ProtocolError::WrongArity("RPOP".into()));
999 }
1000 let key = extract_string(&args[0])?;
1001 Ok(Command::RPop { key })
1002}
1003
1004fn parse_lrange(args: &[Frame]) -> Result<Command, ProtocolError> {
1005 if args.len() != 3 {
1006 return Err(ProtocolError::WrongArity("LRANGE".into()));
1007 }
1008 let key = extract_string(&args[0])?;
1009 let start = parse_i64(&args[1], "LRANGE")?;
1010 let stop = parse_i64(&args[2], "LRANGE")?;
1011 Ok(Command::LRange { key, start, stop })
1012}
1013
1014fn parse_llen(args: &[Frame]) -> Result<Command, ProtocolError> {
1015 if args.len() != 1 {
1016 return Err(ProtocolError::WrongArity("LLEN".into()));
1017 }
1018 let key = extract_string(&args[0])?;
1019 Ok(Command::LLen { key })
1020}
1021
1022fn parse_type(args: &[Frame]) -> Result<Command, ProtocolError> {
1023 if args.len() != 1 {
1024 return Err(ProtocolError::WrongArity("TYPE".into()));
1025 }
1026 let key = extract_string(&args[0])?;
1027 Ok(Command::Type { key })
1028}
1029
1030fn parse_f64(frame: &Frame, cmd: &str) -> Result<f64, ProtocolError> {
1032 let s = extract_string(frame)?;
1033 let v = s.parse::<f64>().map_err(|_| {
1034 ProtocolError::InvalidCommandFrame(format!("value is not a valid float for '{cmd}'"))
1035 })?;
1036 if v.is_nan() {
1037 return Err(ProtocolError::InvalidCommandFrame(format!(
1038 "NaN is not a valid score for '{cmd}'"
1039 )));
1040 }
1041 Ok(v)
1042}
1043
1044fn parse_zadd(args: &[Frame]) -> Result<Command, ProtocolError> {
1045 if args.len() < 3 {
1047 return Err(ProtocolError::WrongArity("ZADD".into()));
1048 }
1049
1050 let key = extract_string(&args[0])?;
1051 let mut flags = ZAddFlags::default();
1052 let mut idx = 1;
1053
1054 while idx < args.len() {
1056 let s = extract_string(&args[idx])?.to_ascii_uppercase();
1057 match s.as_str() {
1058 "NX" => {
1059 flags.nx = true;
1060 idx += 1;
1061 }
1062 "XX" => {
1063 flags.xx = true;
1064 idx += 1;
1065 }
1066 "GT" => {
1067 flags.gt = true;
1068 idx += 1;
1069 }
1070 "LT" => {
1071 flags.lt = true;
1072 idx += 1;
1073 }
1074 "CH" => {
1075 flags.ch = true;
1076 idx += 1;
1077 }
1078 _ => break,
1079 }
1080 }
1081
1082 if flags.nx && flags.xx {
1084 return Err(ProtocolError::InvalidCommandFrame(
1085 "XX and NX options at the same time are not compatible".into(),
1086 ));
1087 }
1088 if flags.gt && flags.lt {
1090 return Err(ProtocolError::InvalidCommandFrame(
1091 "GT and LT options at the same time are not compatible".into(),
1092 ));
1093 }
1094
1095 let remaining = &args[idx..];
1097 if remaining.is_empty() || !remaining.len().is_multiple_of(2) {
1098 return Err(ProtocolError::WrongArity("ZADD".into()));
1099 }
1100
1101 let mut members = Vec::with_capacity(remaining.len() / 2);
1102 for pair in remaining.chunks(2) {
1103 let score = parse_f64(&pair[0], "ZADD")?;
1104 let member = extract_string(&pair[1])?;
1105 members.push((score, member));
1106 }
1107
1108 Ok(Command::ZAdd {
1109 key,
1110 flags,
1111 members,
1112 })
1113}
1114
1115fn parse_zcard(args: &[Frame]) -> Result<Command, ProtocolError> {
1116 if args.len() != 1 {
1117 return Err(ProtocolError::WrongArity("ZCARD".into()));
1118 }
1119 let key = extract_string(&args[0])?;
1120 Ok(Command::ZCard { key })
1121}
1122
1123fn parse_zrem(args: &[Frame]) -> Result<Command, ProtocolError> {
1124 if args.len() < 2 {
1125 return Err(ProtocolError::WrongArity("ZREM".into()));
1126 }
1127 let key = extract_string(&args[0])?;
1128 let members = args[1..]
1129 .iter()
1130 .map(extract_string)
1131 .collect::<Result<Vec<_>, _>>()?;
1132 Ok(Command::ZRem { key, members })
1133}
1134
1135fn parse_zscore(args: &[Frame]) -> Result<Command, ProtocolError> {
1136 if args.len() != 2 {
1137 return Err(ProtocolError::WrongArity("ZSCORE".into()));
1138 }
1139 let key = extract_string(&args[0])?;
1140 let member = extract_string(&args[1])?;
1141 Ok(Command::ZScore { key, member })
1142}
1143
1144fn parse_zrank(args: &[Frame]) -> Result<Command, ProtocolError> {
1145 if args.len() != 2 {
1146 return Err(ProtocolError::WrongArity("ZRANK".into()));
1147 }
1148 let key = extract_string(&args[0])?;
1149 let member = extract_string(&args[1])?;
1150 Ok(Command::ZRank { key, member })
1151}
1152
1153fn parse_zrange(args: &[Frame]) -> Result<Command, ProtocolError> {
1154 if args.len() < 3 || args.len() > 4 {
1155 return Err(ProtocolError::WrongArity("ZRANGE".into()));
1156 }
1157 let key = extract_string(&args[0])?;
1158 let start = parse_i64(&args[1], "ZRANGE")?;
1159 let stop = parse_i64(&args[2], "ZRANGE")?;
1160
1161 let with_scores = if args.len() == 4 {
1162 let opt = extract_string(&args[3])?.to_ascii_uppercase();
1163 if opt != "WITHSCORES" {
1164 return Err(ProtocolError::InvalidCommandFrame(format!(
1165 "unsupported ZRANGE option '{opt}'"
1166 )));
1167 }
1168 true
1169 } else {
1170 false
1171 };
1172
1173 Ok(Command::ZRange {
1174 key,
1175 start,
1176 stop,
1177 with_scores,
1178 })
1179}
1180
1181fn parse_hset(args: &[Frame]) -> Result<Command, ProtocolError> {
1184 if args.len() < 3 || !(args.len() - 1).is_multiple_of(2) {
1188 return Err(ProtocolError::WrongArity("HSET".into()));
1189 }
1190
1191 let key = extract_string(&args[0])?;
1192 let mut fields = Vec::with_capacity((args.len() - 1) / 2);
1193
1194 for chunk in args[1..].chunks(2) {
1195 let field = extract_string(&chunk[0])?;
1196 let value = extract_bytes(&chunk[1])?;
1197 fields.push((field, value));
1198 }
1199
1200 Ok(Command::HSet { key, fields })
1201}
1202
1203fn parse_hget(args: &[Frame]) -> Result<Command, ProtocolError> {
1204 if args.len() != 2 {
1205 return Err(ProtocolError::WrongArity("HGET".into()));
1206 }
1207 let key = extract_string(&args[0])?;
1208 let field = extract_string(&args[1])?;
1209 Ok(Command::HGet { key, field })
1210}
1211
1212fn parse_hgetall(args: &[Frame]) -> Result<Command, ProtocolError> {
1213 if args.len() != 1 {
1214 return Err(ProtocolError::WrongArity("HGETALL".into()));
1215 }
1216 let key = extract_string(&args[0])?;
1217 Ok(Command::HGetAll { key })
1218}
1219
1220fn parse_hdel(args: &[Frame]) -> Result<Command, ProtocolError> {
1221 if args.len() < 2 {
1222 return Err(ProtocolError::WrongArity("HDEL".into()));
1223 }
1224 let key = extract_string(&args[0])?;
1225 let fields = args[1..]
1226 .iter()
1227 .map(extract_string)
1228 .collect::<Result<Vec<_>, _>>()?;
1229 Ok(Command::HDel { key, fields })
1230}
1231
1232fn parse_hexists(args: &[Frame]) -> Result<Command, ProtocolError> {
1233 if args.len() != 2 {
1234 return Err(ProtocolError::WrongArity("HEXISTS".into()));
1235 }
1236 let key = extract_string(&args[0])?;
1237 let field = extract_string(&args[1])?;
1238 Ok(Command::HExists { key, field })
1239}
1240
1241fn parse_hlen(args: &[Frame]) -> Result<Command, ProtocolError> {
1242 if args.len() != 1 {
1243 return Err(ProtocolError::WrongArity("HLEN".into()));
1244 }
1245 let key = extract_string(&args[0])?;
1246 Ok(Command::HLen { key })
1247}
1248
1249fn parse_hincrby(args: &[Frame]) -> Result<Command, ProtocolError> {
1250 if args.len() != 3 {
1251 return Err(ProtocolError::WrongArity("HINCRBY".into()));
1252 }
1253 let key = extract_string(&args[0])?;
1254 let field = extract_string(&args[1])?;
1255 let delta = parse_i64(&args[2], "HINCRBY")?;
1256 Ok(Command::HIncrBy { key, field, delta })
1257}
1258
1259fn parse_hkeys(args: &[Frame]) -> Result<Command, ProtocolError> {
1260 if args.len() != 1 {
1261 return Err(ProtocolError::WrongArity("HKEYS".into()));
1262 }
1263 let key = extract_string(&args[0])?;
1264 Ok(Command::HKeys { key })
1265}
1266
1267fn parse_hvals(args: &[Frame]) -> Result<Command, ProtocolError> {
1268 if args.len() != 1 {
1269 return Err(ProtocolError::WrongArity("HVALS".into()));
1270 }
1271 let key = extract_string(&args[0])?;
1272 Ok(Command::HVals { key })
1273}
1274
1275fn parse_hmget(args: &[Frame]) -> Result<Command, ProtocolError> {
1276 if args.len() < 2 {
1277 return Err(ProtocolError::WrongArity("HMGET".into()));
1278 }
1279 let key = extract_string(&args[0])?;
1280 let fields = args[1..]
1281 .iter()
1282 .map(extract_string)
1283 .collect::<Result<Vec<_>, _>>()?;
1284 Ok(Command::HMGet { key, fields })
1285}
1286
1287fn parse_sadd(args: &[Frame]) -> Result<Command, ProtocolError> {
1290 if args.len() < 2 {
1291 return Err(ProtocolError::WrongArity("SADD".into()));
1292 }
1293 let key = extract_string(&args[0])?;
1294 let members = args[1..]
1295 .iter()
1296 .map(extract_string)
1297 .collect::<Result<Vec<_>, _>>()?;
1298 Ok(Command::SAdd { key, members })
1299}
1300
1301fn parse_srem(args: &[Frame]) -> Result<Command, ProtocolError> {
1302 if args.len() < 2 {
1303 return Err(ProtocolError::WrongArity("SREM".into()));
1304 }
1305 let key = extract_string(&args[0])?;
1306 let members = args[1..]
1307 .iter()
1308 .map(extract_string)
1309 .collect::<Result<Vec<_>, _>>()?;
1310 Ok(Command::SRem { key, members })
1311}
1312
1313fn parse_smembers(args: &[Frame]) -> Result<Command, ProtocolError> {
1314 if args.len() != 1 {
1315 return Err(ProtocolError::WrongArity("SMEMBERS".into()));
1316 }
1317 let key = extract_string(&args[0])?;
1318 Ok(Command::SMembers { key })
1319}
1320
1321fn parse_sismember(args: &[Frame]) -> Result<Command, ProtocolError> {
1322 if args.len() != 2 {
1323 return Err(ProtocolError::WrongArity("SISMEMBER".into()));
1324 }
1325 let key = extract_string(&args[0])?;
1326 let member = extract_string(&args[1])?;
1327 Ok(Command::SIsMember { key, member })
1328}
1329
1330fn parse_scard(args: &[Frame]) -> Result<Command, ProtocolError> {
1331 if args.len() != 1 {
1332 return Err(ProtocolError::WrongArity("SCARD".into()));
1333 }
1334 let key = extract_string(&args[0])?;
1335 Ok(Command::SCard { key })
1336}
1337
1338fn parse_cluster(args: &[Frame]) -> Result<Command, ProtocolError> {
1341 if args.is_empty() {
1342 return Err(ProtocolError::WrongArity("CLUSTER".into()));
1343 }
1344
1345 let subcommand = extract_string(&args[0])?.to_ascii_uppercase();
1346 match subcommand.as_str() {
1347 "INFO" => {
1348 if args.len() != 1 {
1349 return Err(ProtocolError::WrongArity("CLUSTER INFO".into()));
1350 }
1351 Ok(Command::ClusterInfo)
1352 }
1353 "NODES" => {
1354 if args.len() != 1 {
1355 return Err(ProtocolError::WrongArity("CLUSTER NODES".into()));
1356 }
1357 Ok(Command::ClusterNodes)
1358 }
1359 "SLOTS" => {
1360 if args.len() != 1 {
1361 return Err(ProtocolError::WrongArity("CLUSTER SLOTS".into()));
1362 }
1363 Ok(Command::ClusterSlots)
1364 }
1365 "KEYSLOT" => {
1366 if args.len() != 2 {
1367 return Err(ProtocolError::WrongArity("CLUSTER KEYSLOT".into()));
1368 }
1369 let key = extract_string(&args[1])?;
1370 Ok(Command::ClusterKeySlot { key })
1371 }
1372 "MYID" => {
1373 if args.len() != 1 {
1374 return Err(ProtocolError::WrongArity("CLUSTER MYID".into()));
1375 }
1376 Ok(Command::ClusterMyId)
1377 }
1378 "SETSLOT" => parse_cluster_setslot(&args[1..]),
1379 "MEET" => {
1380 if args.len() != 3 {
1381 return Err(ProtocolError::WrongArity("CLUSTER MEET".into()));
1382 }
1383 let ip = extract_string(&args[1])?;
1384 let port_str = extract_string(&args[2])?;
1385 let port: u16 = port_str
1386 .parse()
1387 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid port number".into()))?;
1388 Ok(Command::ClusterMeet { ip, port })
1389 }
1390 "ADDSLOTS" => {
1391 if args.len() < 2 {
1392 return Err(ProtocolError::WrongArity("CLUSTER ADDSLOTS".into()));
1393 }
1394 let slots = parse_slot_list(&args[1..])?;
1395 Ok(Command::ClusterAddSlots { slots })
1396 }
1397 "DELSLOTS" => {
1398 if args.len() < 2 {
1399 return Err(ProtocolError::WrongArity("CLUSTER DELSLOTS".into()));
1400 }
1401 let slots = parse_slot_list(&args[1..])?;
1402 Ok(Command::ClusterDelSlots { slots })
1403 }
1404 "FORGET" => {
1405 if args.len() != 2 {
1406 return Err(ProtocolError::WrongArity("CLUSTER FORGET".into()));
1407 }
1408 let node_id = extract_string(&args[1])?;
1409 Ok(Command::ClusterForget { node_id })
1410 }
1411 "REPLICATE" => {
1412 if args.len() != 2 {
1413 return Err(ProtocolError::WrongArity("CLUSTER REPLICATE".into()));
1414 }
1415 let node_id = extract_string(&args[1])?;
1416 Ok(Command::ClusterReplicate { node_id })
1417 }
1418 "FAILOVER" => {
1419 let mut force = false;
1420 let mut takeover = false;
1421 for arg in &args[1..] {
1422 let opt = extract_string(arg)?.to_ascii_uppercase();
1423 match opt.as_str() {
1424 "FORCE" => force = true,
1425 "TAKEOVER" => takeover = true,
1426 _ => {
1427 return Err(ProtocolError::InvalidCommandFrame(format!(
1428 "unknown CLUSTER FAILOVER option '{opt}'"
1429 )))
1430 }
1431 }
1432 }
1433 Ok(Command::ClusterFailover { force, takeover })
1434 }
1435 "COUNTKEYSINSLOT" => {
1436 if args.len() != 2 {
1437 return Err(ProtocolError::WrongArity("CLUSTER COUNTKEYSINSLOT".into()));
1438 }
1439 let slot_str = extract_string(&args[1])?;
1440 let slot: u16 = slot_str
1441 .parse()
1442 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1443 Ok(Command::ClusterCountKeysInSlot { slot })
1444 }
1445 "GETKEYSINSLOT" => {
1446 if args.len() != 3 {
1447 return Err(ProtocolError::WrongArity("CLUSTER GETKEYSINSLOT".into()));
1448 }
1449 let slot_str = extract_string(&args[1])?;
1450 let slot: u16 = slot_str
1451 .parse()
1452 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1453 let count_str = extract_string(&args[2])?;
1454 let count: u32 = count_str
1455 .parse()
1456 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid count".into()))?;
1457 Ok(Command::ClusterGetKeysInSlot { slot, count })
1458 }
1459 _ => Err(ProtocolError::InvalidCommandFrame(format!(
1460 "unknown CLUSTER subcommand '{subcommand}'"
1461 ))),
1462 }
1463}
1464
1465fn parse_asking(args: &[Frame]) -> Result<Command, ProtocolError> {
1466 if !args.is_empty() {
1467 return Err(ProtocolError::WrongArity("ASKING".into()));
1468 }
1469 Ok(Command::Asking)
1470}
1471
1472fn parse_slowlog(args: &[Frame]) -> Result<Command, ProtocolError> {
1473 if args.is_empty() {
1474 return Err(ProtocolError::WrongArity("SLOWLOG".into()));
1475 }
1476
1477 let subcmd = extract_string(&args[0])?.to_ascii_uppercase();
1478 match subcmd.as_str() {
1479 "GET" => {
1480 let count = if args.len() > 1 {
1481 let n: usize = extract_string(&args[1])?.parse().map_err(|_| {
1482 ProtocolError::InvalidCommandFrame("invalid count for SLOWLOG GET".into())
1483 })?;
1484 Some(n)
1485 } else {
1486 None
1487 };
1488 Ok(Command::SlowLogGet { count })
1489 }
1490 "LEN" => Ok(Command::SlowLogLen),
1491 "RESET" => Ok(Command::SlowLogReset),
1492 other => Err(ProtocolError::InvalidCommandFrame(format!(
1493 "unknown SLOWLOG subcommand '{other}'"
1494 ))),
1495 }
1496}
1497
1498fn parse_slot_list(args: &[Frame]) -> Result<Vec<u16>, ProtocolError> {
1499 let mut slots = Vec::with_capacity(args.len());
1500 for arg in args {
1501 let slot_str = extract_string(arg)?;
1502 let slot: u16 = slot_str
1503 .parse()
1504 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1505 slots.push(slot);
1506 }
1507 Ok(slots)
1508}
1509
1510fn parse_cluster_setslot(args: &[Frame]) -> Result<Command, ProtocolError> {
1511 if args.is_empty() {
1512 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT".into()));
1513 }
1514
1515 let slot_str = extract_string(&args[0])?;
1516 let slot: u16 = slot_str
1517 .parse()
1518 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1519
1520 if args.len() < 2 {
1521 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT".into()));
1522 }
1523
1524 let action = extract_string(&args[1])?.to_ascii_uppercase();
1525 match action.as_str() {
1526 "IMPORTING" => {
1527 if args.len() != 3 {
1528 return Err(ProtocolError::WrongArity(
1529 "CLUSTER SETSLOT IMPORTING".into(),
1530 ));
1531 }
1532 let node_id = extract_string(&args[2])?;
1533 Ok(Command::ClusterSetSlotImporting { slot, node_id })
1534 }
1535 "MIGRATING" => {
1536 if args.len() != 3 {
1537 return Err(ProtocolError::WrongArity(
1538 "CLUSTER SETSLOT MIGRATING".into(),
1539 ));
1540 }
1541 let node_id = extract_string(&args[2])?;
1542 Ok(Command::ClusterSetSlotMigrating { slot, node_id })
1543 }
1544 "NODE" => {
1545 if args.len() != 3 {
1546 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT NODE".into()));
1547 }
1548 let node_id = extract_string(&args[2])?;
1549 Ok(Command::ClusterSetSlotNode { slot, node_id })
1550 }
1551 "STABLE" => {
1552 if args.len() != 2 {
1553 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT STABLE".into()));
1554 }
1555 Ok(Command::ClusterSetSlotStable { slot })
1556 }
1557 _ => Err(ProtocolError::InvalidCommandFrame(format!(
1558 "unknown CLUSTER SETSLOT action '{action}'"
1559 ))),
1560 }
1561}
1562
1563fn parse_migrate(args: &[Frame]) -> Result<Command, ProtocolError> {
1564 if args.len() < 5 {
1566 return Err(ProtocolError::WrongArity("MIGRATE".into()));
1567 }
1568
1569 let host = extract_string(&args[0])?;
1570 let port_str = extract_string(&args[1])?;
1571 let port: u16 = port_str
1572 .parse()
1573 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid port number".into()))?;
1574 let key = extract_string(&args[2])?;
1575 let db_str = extract_string(&args[3])?;
1576 let db: u32 = db_str
1577 .parse()
1578 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid db number".into()))?;
1579 let timeout_str = extract_string(&args[4])?;
1580 let timeout_ms: u64 = timeout_str
1581 .parse()
1582 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid timeout".into()))?;
1583
1584 let mut copy = false;
1585 let mut replace = false;
1586
1587 for arg in &args[5..] {
1588 let opt = extract_string(arg)?.to_ascii_uppercase();
1589 match opt.as_str() {
1590 "COPY" => copy = true,
1591 "REPLACE" => replace = true,
1592 _ => {
1593 return Err(ProtocolError::InvalidCommandFrame(format!(
1594 "unknown MIGRATE option '{opt}'"
1595 )))
1596 }
1597 }
1598 }
1599
1600 Ok(Command::Migrate {
1601 host,
1602 port,
1603 key,
1604 db,
1605 timeout_ms,
1606 copy,
1607 replace,
1608 })
1609}
1610
1611fn parse_subscribe(args: &[Frame]) -> Result<Command, ProtocolError> {
1612 if args.is_empty() {
1613 return Err(ProtocolError::WrongArity("SUBSCRIBE".into()));
1614 }
1615 let channels: Vec<String> = args.iter().map(extract_string).collect::<Result<_, _>>()?;
1616 Ok(Command::Subscribe { channels })
1617}
1618
1619fn parse_unsubscribe(args: &[Frame]) -> Result<Command, ProtocolError> {
1620 let channels: Vec<String> = args.iter().map(extract_string).collect::<Result<_, _>>()?;
1621 Ok(Command::Unsubscribe { channels })
1622}
1623
1624fn parse_psubscribe(args: &[Frame]) -> Result<Command, ProtocolError> {
1625 if args.is_empty() {
1626 return Err(ProtocolError::WrongArity("PSUBSCRIBE".into()));
1627 }
1628 let patterns: Vec<String> = args.iter().map(extract_string).collect::<Result<_, _>>()?;
1629 Ok(Command::PSubscribe { patterns })
1630}
1631
1632fn parse_punsubscribe(args: &[Frame]) -> Result<Command, ProtocolError> {
1633 let patterns: Vec<String> = args.iter().map(extract_string).collect::<Result<_, _>>()?;
1634 Ok(Command::PUnsubscribe { patterns })
1635}
1636
1637fn parse_publish(args: &[Frame]) -> Result<Command, ProtocolError> {
1638 if args.len() != 2 {
1639 return Err(ProtocolError::WrongArity("PUBLISH".into()));
1640 }
1641 let channel = extract_string(&args[0])?;
1642 let message = extract_bytes(&args[1])?;
1643 Ok(Command::Publish { channel, message })
1644}
1645
1646fn parse_pubsub(args: &[Frame]) -> Result<Command, ProtocolError> {
1647 if args.is_empty() {
1648 return Err(ProtocolError::WrongArity("PUBSUB".into()));
1649 }
1650
1651 let subcmd = extract_string(&args[0])?.to_ascii_uppercase();
1652 match subcmd.as_str() {
1653 "CHANNELS" => {
1654 let pattern = if args.len() > 1 {
1655 Some(extract_string(&args[1])?)
1656 } else {
1657 None
1658 };
1659 Ok(Command::PubSubChannels { pattern })
1660 }
1661 "NUMSUB" => {
1662 let channels: Vec<String> = args[1..]
1663 .iter()
1664 .map(extract_string)
1665 .collect::<Result<_, _>>()?;
1666 Ok(Command::PubSubNumSub { channels })
1667 }
1668 "NUMPAT" => Ok(Command::PubSubNumPat),
1669 other => Err(ProtocolError::InvalidCommandFrame(format!(
1670 "unknown PUBSUB subcommand '{other}'"
1671 ))),
1672 }
1673}
1674
1675fn parse_auth(args: &[Frame]) -> Result<Command, ProtocolError> {
1676 match args.len() {
1677 1 => {
1678 let password = extract_string(&args[0])?;
1679 Ok(Command::Auth {
1680 username: None,
1681 password,
1682 })
1683 }
1684 2 => {
1685 let username = extract_string(&args[0])?;
1686 let password = extract_string(&args[1])?;
1687 Ok(Command::Auth {
1688 username: Some(username),
1689 password,
1690 })
1691 }
1692 _ => Err(ProtocolError::WrongArity("AUTH".into())),
1693 }
1694}
1695
1696fn parse_quit(args: &[Frame]) -> Result<Command, ProtocolError> {
1697 if !args.is_empty() {
1698 return Err(ProtocolError::WrongArity("QUIT".into()));
1699 }
1700 Ok(Command::Quit)
1701}
1702
1703#[cfg(test)]
1704mod tests {
1705 use super::*;
1706
1707 fn cmd(parts: &[&str]) -> Frame {
1709 Frame::Array(
1710 parts
1711 .iter()
1712 .map(|s| Frame::Bulk(Bytes::from(s.to_string())))
1713 .collect(),
1714 )
1715 }
1716
1717 #[test]
1720 fn ping_no_args() {
1721 assert_eq!(
1722 Command::from_frame(cmd(&["PING"])).unwrap(),
1723 Command::Ping(None),
1724 );
1725 }
1726
1727 #[test]
1728 fn ping_with_message() {
1729 assert_eq!(
1730 Command::from_frame(cmd(&["PING", "hello"])).unwrap(),
1731 Command::Ping(Some(Bytes::from("hello"))),
1732 );
1733 }
1734
1735 #[test]
1736 fn ping_case_insensitive() {
1737 assert_eq!(
1738 Command::from_frame(cmd(&["ping"])).unwrap(),
1739 Command::Ping(None),
1740 );
1741 assert_eq!(
1742 Command::from_frame(cmd(&["Ping"])).unwrap(),
1743 Command::Ping(None),
1744 );
1745 }
1746
1747 #[test]
1748 fn ping_too_many_args() {
1749 let err = Command::from_frame(cmd(&["PING", "a", "b"])).unwrap_err();
1750 assert!(matches!(err, ProtocolError::WrongArity(_)));
1751 }
1752
1753 #[test]
1756 fn echo() {
1757 assert_eq!(
1758 Command::from_frame(cmd(&["ECHO", "test"])).unwrap(),
1759 Command::Echo(Bytes::from("test")),
1760 );
1761 }
1762
1763 #[test]
1764 fn echo_missing_arg() {
1765 let err = Command::from_frame(cmd(&["ECHO"])).unwrap_err();
1766 assert!(matches!(err, ProtocolError::WrongArity(_)));
1767 }
1768
1769 #[test]
1772 fn get_basic() {
1773 assert_eq!(
1774 Command::from_frame(cmd(&["GET", "mykey"])).unwrap(),
1775 Command::Get {
1776 key: "mykey".into()
1777 },
1778 );
1779 }
1780
1781 #[test]
1782 fn get_no_args() {
1783 let err = Command::from_frame(cmd(&["GET"])).unwrap_err();
1784 assert!(matches!(err, ProtocolError::WrongArity(_)));
1785 }
1786
1787 #[test]
1788 fn get_too_many_args() {
1789 let err = Command::from_frame(cmd(&["GET", "a", "b"])).unwrap_err();
1790 assert!(matches!(err, ProtocolError::WrongArity(_)));
1791 }
1792
1793 #[test]
1794 fn get_case_insensitive() {
1795 assert_eq!(
1796 Command::from_frame(cmd(&["get", "k"])).unwrap(),
1797 Command::Get { key: "k".into() },
1798 );
1799 }
1800
1801 #[test]
1804 fn set_basic() {
1805 assert_eq!(
1806 Command::from_frame(cmd(&["SET", "key", "value"])).unwrap(),
1807 Command::Set {
1808 key: "key".into(),
1809 value: Bytes::from("value"),
1810 expire: None,
1811 nx: false,
1812 xx: false,
1813 },
1814 );
1815 }
1816
1817 #[test]
1818 fn set_with_ex() {
1819 assert_eq!(
1820 Command::from_frame(cmd(&["SET", "key", "val", "EX", "10"])).unwrap(),
1821 Command::Set {
1822 key: "key".into(),
1823 value: Bytes::from("val"),
1824 expire: Some(SetExpire::Ex(10)),
1825 nx: false,
1826 xx: false,
1827 },
1828 );
1829 }
1830
1831 #[test]
1832 fn set_with_px() {
1833 assert_eq!(
1834 Command::from_frame(cmd(&["SET", "key", "val", "PX", "5000"])).unwrap(),
1835 Command::Set {
1836 key: "key".into(),
1837 value: Bytes::from("val"),
1838 expire: Some(SetExpire::Px(5000)),
1839 nx: false,
1840 xx: false,
1841 },
1842 );
1843 }
1844
1845 #[test]
1846 fn set_ex_case_insensitive() {
1847 assert_eq!(
1848 Command::from_frame(cmd(&["set", "k", "v", "ex", "5"])).unwrap(),
1849 Command::Set {
1850 key: "k".into(),
1851 value: Bytes::from("v"),
1852 expire: Some(SetExpire::Ex(5)),
1853 nx: false,
1854 xx: false,
1855 },
1856 );
1857 }
1858
1859 #[test]
1860 fn set_nx_flag() {
1861 assert_eq!(
1862 Command::from_frame(cmd(&["SET", "key", "val", "NX"])).unwrap(),
1863 Command::Set {
1864 key: "key".into(),
1865 value: Bytes::from("val"),
1866 expire: None,
1867 nx: true,
1868 xx: false,
1869 },
1870 );
1871 }
1872
1873 #[test]
1874 fn set_xx_flag() {
1875 assert_eq!(
1876 Command::from_frame(cmd(&["SET", "key", "val", "XX"])).unwrap(),
1877 Command::Set {
1878 key: "key".into(),
1879 value: Bytes::from("val"),
1880 expire: None,
1881 nx: false,
1882 xx: true,
1883 },
1884 );
1885 }
1886
1887 #[test]
1888 fn set_nx_with_ex() {
1889 assert_eq!(
1890 Command::from_frame(cmd(&["SET", "key", "val", "EX", "10", "NX"])).unwrap(),
1891 Command::Set {
1892 key: "key".into(),
1893 value: Bytes::from("val"),
1894 expire: Some(SetExpire::Ex(10)),
1895 nx: true,
1896 xx: false,
1897 },
1898 );
1899 }
1900
1901 #[test]
1902 fn set_nx_before_ex() {
1903 assert_eq!(
1904 Command::from_frame(cmd(&["SET", "key", "val", "NX", "PX", "5000"])).unwrap(),
1905 Command::Set {
1906 key: "key".into(),
1907 value: Bytes::from("val"),
1908 expire: Some(SetExpire::Px(5000)),
1909 nx: true,
1910 xx: false,
1911 },
1912 );
1913 }
1914
1915 #[test]
1916 fn set_nx_xx_conflict() {
1917 let err = Command::from_frame(cmd(&["SET", "k", "v", "NX", "XX"])).unwrap_err();
1918 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1919 }
1920
1921 #[test]
1922 fn set_nx_case_insensitive() {
1923 assert_eq!(
1924 Command::from_frame(cmd(&["set", "k", "v", "nx"])).unwrap(),
1925 Command::Set {
1926 key: "k".into(),
1927 value: Bytes::from("v"),
1928 expire: None,
1929 nx: true,
1930 xx: false,
1931 },
1932 );
1933 }
1934
1935 #[test]
1936 fn set_missing_value() {
1937 let err = Command::from_frame(cmd(&["SET", "key"])).unwrap_err();
1938 assert!(matches!(err, ProtocolError::WrongArity(_)));
1939 }
1940
1941 #[test]
1942 fn set_invalid_expire_value() {
1943 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX", "notanum"])).unwrap_err();
1944 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1945 }
1946
1947 #[test]
1948 fn set_zero_expire() {
1949 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX", "0"])).unwrap_err();
1950 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1951 }
1952
1953 #[test]
1954 fn set_unknown_flag() {
1955 let err = Command::from_frame(cmd(&["SET", "k", "v", "ZZ", "10"])).unwrap_err();
1956 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1957 }
1958
1959 #[test]
1960 fn set_incomplete_expire() {
1961 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX"])).unwrap_err();
1963 assert!(matches!(err, ProtocolError::WrongArity(_)));
1964 }
1965
1966 #[test]
1969 fn del_single() {
1970 assert_eq!(
1971 Command::from_frame(cmd(&["DEL", "key"])).unwrap(),
1972 Command::Del {
1973 keys: vec!["key".into()]
1974 },
1975 );
1976 }
1977
1978 #[test]
1979 fn del_multiple() {
1980 assert_eq!(
1981 Command::from_frame(cmd(&["DEL", "a", "b", "c"])).unwrap(),
1982 Command::Del {
1983 keys: vec!["a".into(), "b".into(), "c".into()]
1984 },
1985 );
1986 }
1987
1988 #[test]
1989 fn del_no_args() {
1990 let err = Command::from_frame(cmd(&["DEL"])).unwrap_err();
1991 assert!(matches!(err, ProtocolError::WrongArity(_)));
1992 }
1993
1994 #[test]
1997 fn exists_single() {
1998 assert_eq!(
1999 Command::from_frame(cmd(&["EXISTS", "key"])).unwrap(),
2000 Command::Exists {
2001 keys: vec!["key".into()]
2002 },
2003 );
2004 }
2005
2006 #[test]
2007 fn exists_multiple() {
2008 assert_eq!(
2009 Command::from_frame(cmd(&["EXISTS", "a", "b"])).unwrap(),
2010 Command::Exists {
2011 keys: vec!["a".into(), "b".into()]
2012 },
2013 );
2014 }
2015
2016 #[test]
2017 fn exists_no_args() {
2018 let err = Command::from_frame(cmd(&["EXISTS"])).unwrap_err();
2019 assert!(matches!(err, ProtocolError::WrongArity(_)));
2020 }
2021
2022 #[test]
2025 fn mget_single() {
2026 assert_eq!(
2027 Command::from_frame(cmd(&["MGET", "key"])).unwrap(),
2028 Command::MGet {
2029 keys: vec!["key".into()]
2030 },
2031 );
2032 }
2033
2034 #[test]
2035 fn mget_multiple() {
2036 assert_eq!(
2037 Command::from_frame(cmd(&["MGET", "a", "b", "c"])).unwrap(),
2038 Command::MGet {
2039 keys: vec!["a".into(), "b".into(), "c".into()]
2040 },
2041 );
2042 }
2043
2044 #[test]
2045 fn mget_no_args() {
2046 let err = Command::from_frame(cmd(&["MGET"])).unwrap_err();
2047 assert!(matches!(err, ProtocolError::WrongArity(_)));
2048 }
2049
2050 #[test]
2053 fn mset_single_pair() {
2054 assert_eq!(
2055 Command::from_frame(cmd(&["MSET", "key", "val"])).unwrap(),
2056 Command::MSet {
2057 pairs: vec![("key".into(), Bytes::from("val"))]
2058 },
2059 );
2060 }
2061
2062 #[test]
2063 fn mset_multiple_pairs() {
2064 assert_eq!(
2065 Command::from_frame(cmd(&["MSET", "a", "1", "b", "2"])).unwrap(),
2066 Command::MSet {
2067 pairs: vec![
2068 ("a".into(), Bytes::from("1")),
2069 ("b".into(), Bytes::from("2")),
2070 ]
2071 },
2072 );
2073 }
2074
2075 #[test]
2076 fn mset_no_args() {
2077 let err = Command::from_frame(cmd(&["MSET"])).unwrap_err();
2078 assert!(matches!(err, ProtocolError::WrongArity(_)));
2079 }
2080
2081 #[test]
2082 fn mset_odd_args() {
2083 let err = Command::from_frame(cmd(&["MSET", "a", "1", "b"])).unwrap_err();
2085 assert!(matches!(err, ProtocolError::WrongArity(_)));
2086 }
2087
2088 #[test]
2091 fn expire_basic() {
2092 assert_eq!(
2093 Command::from_frame(cmd(&["EXPIRE", "key", "60"])).unwrap(),
2094 Command::Expire {
2095 key: "key".into(),
2096 seconds: 60,
2097 },
2098 );
2099 }
2100
2101 #[test]
2102 fn expire_wrong_arity() {
2103 let err = Command::from_frame(cmd(&["EXPIRE", "key"])).unwrap_err();
2104 assert!(matches!(err, ProtocolError::WrongArity(_)));
2105 }
2106
2107 #[test]
2108 fn expire_invalid_seconds() {
2109 let err = Command::from_frame(cmd(&["EXPIRE", "key", "abc"])).unwrap_err();
2110 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2111 }
2112
2113 #[test]
2114 fn expire_zero_seconds() {
2115 let err = Command::from_frame(cmd(&["EXPIRE", "key", "0"])).unwrap_err();
2116 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2117 }
2118
2119 #[test]
2122 fn ttl_basic() {
2123 assert_eq!(
2124 Command::from_frame(cmd(&["TTL", "key"])).unwrap(),
2125 Command::Ttl { key: "key".into() },
2126 );
2127 }
2128
2129 #[test]
2130 fn ttl_wrong_arity() {
2131 let err = Command::from_frame(cmd(&["TTL"])).unwrap_err();
2132 assert!(matches!(err, ProtocolError::WrongArity(_)));
2133 }
2134
2135 #[test]
2138 fn dbsize_basic() {
2139 assert_eq!(
2140 Command::from_frame(cmd(&["DBSIZE"])).unwrap(),
2141 Command::DbSize,
2142 );
2143 }
2144
2145 #[test]
2146 fn dbsize_case_insensitive() {
2147 assert_eq!(
2148 Command::from_frame(cmd(&["dbsize"])).unwrap(),
2149 Command::DbSize,
2150 );
2151 }
2152
2153 #[test]
2154 fn dbsize_extra_args() {
2155 let err = Command::from_frame(cmd(&["DBSIZE", "extra"])).unwrap_err();
2156 assert!(matches!(err, ProtocolError::WrongArity(_)));
2157 }
2158
2159 #[test]
2162 fn info_no_section() {
2163 assert_eq!(
2164 Command::from_frame(cmd(&["INFO"])).unwrap(),
2165 Command::Info { section: None },
2166 );
2167 }
2168
2169 #[test]
2170 fn info_with_section() {
2171 assert_eq!(
2172 Command::from_frame(cmd(&["INFO", "keyspace"])).unwrap(),
2173 Command::Info {
2174 section: Some("keyspace".into())
2175 },
2176 );
2177 }
2178
2179 #[test]
2180 fn info_too_many_args() {
2181 let err = Command::from_frame(cmd(&["INFO", "a", "b"])).unwrap_err();
2182 assert!(matches!(err, ProtocolError::WrongArity(_)));
2183 }
2184
2185 #[test]
2188 fn bgsave_basic() {
2189 assert_eq!(
2190 Command::from_frame(cmd(&["BGSAVE"])).unwrap(),
2191 Command::BgSave,
2192 );
2193 }
2194
2195 #[test]
2196 fn bgsave_case_insensitive() {
2197 assert_eq!(
2198 Command::from_frame(cmd(&["bgsave"])).unwrap(),
2199 Command::BgSave,
2200 );
2201 }
2202
2203 #[test]
2204 fn bgsave_extra_args() {
2205 let err = Command::from_frame(cmd(&["BGSAVE", "extra"])).unwrap_err();
2206 assert!(matches!(err, ProtocolError::WrongArity(_)));
2207 }
2208
2209 #[test]
2212 fn bgrewriteaof_basic() {
2213 assert_eq!(
2214 Command::from_frame(cmd(&["BGREWRITEAOF"])).unwrap(),
2215 Command::BgRewriteAof,
2216 );
2217 }
2218
2219 #[test]
2220 fn bgrewriteaof_case_insensitive() {
2221 assert_eq!(
2222 Command::from_frame(cmd(&["bgrewriteaof"])).unwrap(),
2223 Command::BgRewriteAof,
2224 );
2225 }
2226
2227 #[test]
2228 fn bgrewriteaof_extra_args() {
2229 let err = Command::from_frame(cmd(&["BGREWRITEAOF", "extra"])).unwrap_err();
2230 assert!(matches!(err, ProtocolError::WrongArity(_)));
2231 }
2232
2233 #[test]
2236 fn flushdb_basic() {
2237 assert_eq!(
2238 Command::from_frame(cmd(&["FLUSHDB"])).unwrap(),
2239 Command::FlushDb,
2240 );
2241 }
2242
2243 #[test]
2244 fn flushdb_case_insensitive() {
2245 assert_eq!(
2246 Command::from_frame(cmd(&["flushdb"])).unwrap(),
2247 Command::FlushDb,
2248 );
2249 }
2250
2251 #[test]
2252 fn flushdb_extra_args() {
2253 let err = Command::from_frame(cmd(&["FLUSHDB", "extra"])).unwrap_err();
2254 assert!(matches!(err, ProtocolError::WrongArity(_)));
2255 }
2256
2257 #[test]
2260 fn lpush_single() {
2261 assert_eq!(
2262 Command::from_frame(cmd(&["LPUSH", "list", "val"])).unwrap(),
2263 Command::LPush {
2264 key: "list".into(),
2265 values: vec![Bytes::from("val")],
2266 },
2267 );
2268 }
2269
2270 #[test]
2271 fn lpush_multiple() {
2272 assert_eq!(
2273 Command::from_frame(cmd(&["LPUSH", "list", "a", "b", "c"])).unwrap(),
2274 Command::LPush {
2275 key: "list".into(),
2276 values: vec![Bytes::from("a"), Bytes::from("b"), Bytes::from("c")],
2277 },
2278 );
2279 }
2280
2281 #[test]
2282 fn lpush_no_value() {
2283 let err = Command::from_frame(cmd(&["LPUSH", "key"])).unwrap_err();
2284 assert!(matches!(err, ProtocolError::WrongArity(_)));
2285 }
2286
2287 #[test]
2288 fn lpush_case_insensitive() {
2289 assert!(matches!(
2290 Command::from_frame(cmd(&["lpush", "k", "v"])).unwrap(),
2291 Command::LPush { .. }
2292 ));
2293 }
2294
2295 #[test]
2298 fn rpush_single() {
2299 assert_eq!(
2300 Command::from_frame(cmd(&["RPUSH", "list", "val"])).unwrap(),
2301 Command::RPush {
2302 key: "list".into(),
2303 values: vec![Bytes::from("val")],
2304 },
2305 );
2306 }
2307
2308 #[test]
2309 fn rpush_no_value() {
2310 let err = Command::from_frame(cmd(&["RPUSH", "key"])).unwrap_err();
2311 assert!(matches!(err, ProtocolError::WrongArity(_)));
2312 }
2313
2314 #[test]
2317 fn lpop_basic() {
2318 assert_eq!(
2319 Command::from_frame(cmd(&["LPOP", "list"])).unwrap(),
2320 Command::LPop { key: "list".into() },
2321 );
2322 }
2323
2324 #[test]
2325 fn lpop_wrong_arity() {
2326 let err = Command::from_frame(cmd(&["LPOP"])).unwrap_err();
2327 assert!(matches!(err, ProtocolError::WrongArity(_)));
2328 }
2329
2330 #[test]
2333 fn rpop_basic() {
2334 assert_eq!(
2335 Command::from_frame(cmd(&["RPOP", "list"])).unwrap(),
2336 Command::RPop { key: "list".into() },
2337 );
2338 }
2339
2340 #[test]
2343 fn lrange_basic() {
2344 assert_eq!(
2345 Command::from_frame(cmd(&["LRANGE", "list", "0", "-1"])).unwrap(),
2346 Command::LRange {
2347 key: "list".into(),
2348 start: 0,
2349 stop: -1,
2350 },
2351 );
2352 }
2353
2354 #[test]
2355 fn lrange_wrong_arity() {
2356 let err = Command::from_frame(cmd(&["LRANGE", "list", "0"])).unwrap_err();
2357 assert!(matches!(err, ProtocolError::WrongArity(_)));
2358 }
2359
2360 #[test]
2361 fn lrange_invalid_index() {
2362 let err = Command::from_frame(cmd(&["LRANGE", "list", "abc", "0"])).unwrap_err();
2363 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2364 }
2365
2366 #[test]
2369 fn llen_basic() {
2370 assert_eq!(
2371 Command::from_frame(cmd(&["LLEN", "list"])).unwrap(),
2372 Command::LLen { key: "list".into() },
2373 );
2374 }
2375
2376 #[test]
2377 fn llen_wrong_arity() {
2378 let err = Command::from_frame(cmd(&["LLEN"])).unwrap_err();
2379 assert!(matches!(err, ProtocolError::WrongArity(_)));
2380 }
2381
2382 #[test]
2385 fn type_basic() {
2386 assert_eq!(
2387 Command::from_frame(cmd(&["TYPE", "key"])).unwrap(),
2388 Command::Type { key: "key".into() },
2389 );
2390 }
2391
2392 #[test]
2393 fn type_wrong_arity() {
2394 let err = Command::from_frame(cmd(&["TYPE"])).unwrap_err();
2395 assert!(matches!(err, ProtocolError::WrongArity(_)));
2396 }
2397
2398 #[test]
2399 fn type_case_insensitive() {
2400 assert!(matches!(
2401 Command::from_frame(cmd(&["type", "k"])).unwrap(),
2402 Command::Type { .. }
2403 ));
2404 }
2405
2406 #[test]
2409 fn unknown_command() {
2410 assert_eq!(
2411 Command::from_frame(cmd(&["FOOBAR", "arg"])).unwrap(),
2412 Command::Unknown("FOOBAR".into()),
2413 );
2414 }
2415
2416 #[test]
2417 fn non_array_frame() {
2418 let err = Command::from_frame(Frame::Simple("PING".into())).unwrap_err();
2419 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2420 }
2421
2422 #[test]
2423 fn empty_array() {
2424 let err = Command::from_frame(Frame::Array(vec![])).unwrap_err();
2425 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2426 }
2427
2428 #[test]
2431 fn zadd_basic() {
2432 let parsed = Command::from_frame(cmd(&["ZADD", "board", "100", "alice"])).unwrap();
2433 match parsed {
2434 Command::ZAdd {
2435 key,
2436 flags,
2437 members,
2438 } => {
2439 assert_eq!(key, "board");
2440 assert_eq!(flags, ZAddFlags::default());
2441 assert_eq!(members, vec![(100.0, "alice".into())]);
2442 }
2443 other => panic!("expected ZAdd, got {other:?}"),
2444 }
2445 }
2446
2447 #[test]
2448 fn zadd_multiple_members() {
2449 let parsed =
2450 Command::from_frame(cmd(&["ZADD", "board", "100", "alice", "200", "bob"])).unwrap();
2451 match parsed {
2452 Command::ZAdd { members, .. } => {
2453 assert_eq!(members.len(), 2);
2454 assert_eq!(members[0], (100.0, "alice".into()));
2455 assert_eq!(members[1], (200.0, "bob".into()));
2456 }
2457 other => panic!("expected ZAdd, got {other:?}"),
2458 }
2459 }
2460
2461 #[test]
2462 fn zadd_with_flags() {
2463 let parsed = Command::from_frame(cmd(&["ZADD", "z", "NX", "CH", "100", "alice"])).unwrap();
2464 match parsed {
2465 Command::ZAdd { flags, .. } => {
2466 assert!(flags.nx);
2467 assert!(flags.ch);
2468 assert!(!flags.xx);
2469 assert!(!flags.gt);
2470 assert!(!flags.lt);
2471 }
2472 other => panic!("expected ZAdd, got {other:?}"),
2473 }
2474 }
2475
2476 #[test]
2477 fn zadd_gt_flag() {
2478 let parsed = Command::from_frame(cmd(&["zadd", "z", "gt", "100", "alice"])).unwrap();
2479 match parsed {
2480 Command::ZAdd { flags, .. } => assert!(flags.gt),
2481 other => panic!("expected ZAdd, got {other:?}"),
2482 }
2483 }
2484
2485 #[test]
2486 fn zadd_nx_xx_conflict() {
2487 let err = Command::from_frame(cmd(&["ZADD", "z", "NX", "XX", "100", "alice"])).unwrap_err();
2488 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2489 }
2490
2491 #[test]
2492 fn zadd_gt_lt_conflict() {
2493 let err = Command::from_frame(cmd(&["ZADD", "z", "GT", "LT", "100", "alice"])).unwrap_err();
2494 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2495 }
2496
2497 #[test]
2498 fn zadd_wrong_arity() {
2499 let err = Command::from_frame(cmd(&["ZADD", "z"])).unwrap_err();
2500 assert!(matches!(err, ProtocolError::WrongArity(_)));
2501 }
2502
2503 #[test]
2504 fn zadd_odd_score_member_count() {
2505 let err = Command::from_frame(cmd(&["ZADD", "z", "100"])).unwrap_err();
2507 assert!(matches!(err, ProtocolError::WrongArity(_)));
2508 }
2509
2510 #[test]
2511 fn zadd_invalid_score() {
2512 let err = Command::from_frame(cmd(&["ZADD", "z", "notanum", "alice"])).unwrap_err();
2513 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2514 }
2515
2516 #[test]
2517 fn zadd_nan_score() {
2518 let err = Command::from_frame(cmd(&["ZADD", "z", "nan", "alice"])).unwrap_err();
2519 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2520 }
2521
2522 #[test]
2523 fn zadd_negative_score() {
2524 let parsed = Command::from_frame(cmd(&["ZADD", "z", "-50.5", "alice"])).unwrap();
2525 match parsed {
2526 Command::ZAdd { members, .. } => {
2527 assert_eq!(members[0].0, -50.5);
2528 }
2529 other => panic!("expected ZAdd, got {other:?}"),
2530 }
2531 }
2532
2533 #[test]
2536 fn zrem_basic() {
2537 assert_eq!(
2538 Command::from_frame(cmd(&["ZREM", "z", "alice"])).unwrap(),
2539 Command::ZRem {
2540 key: "z".into(),
2541 members: vec!["alice".into()],
2542 },
2543 );
2544 }
2545
2546 #[test]
2547 fn zrem_multiple() {
2548 let parsed = Command::from_frame(cmd(&["ZREM", "z", "a", "b", "c"])).unwrap();
2549 match parsed {
2550 Command::ZRem { members, .. } => assert_eq!(members.len(), 3),
2551 other => panic!("expected ZRem, got {other:?}"),
2552 }
2553 }
2554
2555 #[test]
2556 fn zrem_wrong_arity() {
2557 let err = Command::from_frame(cmd(&["ZREM", "z"])).unwrap_err();
2558 assert!(matches!(err, ProtocolError::WrongArity(_)));
2559 }
2560
2561 #[test]
2564 fn zscore_basic() {
2565 assert_eq!(
2566 Command::from_frame(cmd(&["ZSCORE", "z", "alice"])).unwrap(),
2567 Command::ZScore {
2568 key: "z".into(),
2569 member: "alice".into(),
2570 },
2571 );
2572 }
2573
2574 #[test]
2575 fn zscore_wrong_arity() {
2576 let err = Command::from_frame(cmd(&["ZSCORE", "z"])).unwrap_err();
2577 assert!(matches!(err, ProtocolError::WrongArity(_)));
2578 }
2579
2580 #[test]
2583 fn zrank_basic() {
2584 assert_eq!(
2585 Command::from_frame(cmd(&["ZRANK", "z", "alice"])).unwrap(),
2586 Command::ZRank {
2587 key: "z".into(),
2588 member: "alice".into(),
2589 },
2590 );
2591 }
2592
2593 #[test]
2594 fn zrank_wrong_arity() {
2595 let err = Command::from_frame(cmd(&["ZRANK", "z"])).unwrap_err();
2596 assert!(matches!(err, ProtocolError::WrongArity(_)));
2597 }
2598
2599 #[test]
2602 fn zcard_basic() {
2603 assert_eq!(
2604 Command::from_frame(cmd(&["ZCARD", "z"])).unwrap(),
2605 Command::ZCard { key: "z".into() },
2606 );
2607 }
2608
2609 #[test]
2610 fn zcard_wrong_arity() {
2611 let err = Command::from_frame(cmd(&["ZCARD"])).unwrap_err();
2612 assert!(matches!(err, ProtocolError::WrongArity(_)));
2613 }
2614
2615 #[test]
2618 fn zrange_basic() {
2619 assert_eq!(
2620 Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1"])).unwrap(),
2621 Command::ZRange {
2622 key: "z".into(),
2623 start: 0,
2624 stop: -1,
2625 with_scores: false,
2626 },
2627 );
2628 }
2629
2630 #[test]
2631 fn zrange_with_scores() {
2632 assert_eq!(
2633 Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1", "WITHSCORES"])).unwrap(),
2634 Command::ZRange {
2635 key: "z".into(),
2636 start: 0,
2637 stop: -1,
2638 with_scores: true,
2639 },
2640 );
2641 }
2642
2643 #[test]
2644 fn zrange_withscores_case_insensitive() {
2645 assert_eq!(
2646 Command::from_frame(cmd(&["zrange", "z", "0", "-1", "withscores"])).unwrap(),
2647 Command::ZRange {
2648 key: "z".into(),
2649 start: 0,
2650 stop: -1,
2651 with_scores: true,
2652 },
2653 );
2654 }
2655
2656 #[test]
2657 fn zrange_invalid_option() {
2658 let err = Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1", "BADOPT"])).unwrap_err();
2659 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2660 }
2661
2662 #[test]
2663 fn zrange_wrong_arity() {
2664 let err = Command::from_frame(cmd(&["ZRANGE", "z", "0"])).unwrap_err();
2665 assert!(matches!(err, ProtocolError::WrongArity(_)));
2666 }
2667
2668 #[test]
2671 fn incr_basic() {
2672 assert_eq!(
2673 Command::from_frame(cmd(&["INCR", "counter"])).unwrap(),
2674 Command::Incr {
2675 key: "counter".into()
2676 },
2677 );
2678 }
2679
2680 #[test]
2681 fn incr_wrong_arity() {
2682 let err = Command::from_frame(cmd(&["INCR"])).unwrap_err();
2683 assert!(matches!(err, ProtocolError::WrongArity(_)));
2684 }
2685
2686 #[test]
2689 fn decr_basic() {
2690 assert_eq!(
2691 Command::from_frame(cmd(&["DECR", "counter"])).unwrap(),
2692 Command::Decr {
2693 key: "counter".into()
2694 },
2695 );
2696 }
2697
2698 #[test]
2699 fn decr_wrong_arity() {
2700 let err = Command::from_frame(cmd(&["DECR"])).unwrap_err();
2701 assert!(matches!(err, ProtocolError::WrongArity(_)));
2702 }
2703
2704 #[test]
2707 fn persist_basic() {
2708 assert_eq!(
2709 Command::from_frame(cmd(&["PERSIST", "key"])).unwrap(),
2710 Command::Persist { key: "key".into() },
2711 );
2712 }
2713
2714 #[test]
2715 fn persist_case_insensitive() {
2716 assert_eq!(
2717 Command::from_frame(cmd(&["persist", "key"])).unwrap(),
2718 Command::Persist { key: "key".into() },
2719 );
2720 }
2721
2722 #[test]
2723 fn persist_wrong_arity() {
2724 let err = Command::from_frame(cmd(&["PERSIST"])).unwrap_err();
2725 assert!(matches!(err, ProtocolError::WrongArity(_)));
2726 }
2727
2728 #[test]
2731 fn pttl_basic() {
2732 assert_eq!(
2733 Command::from_frame(cmd(&["PTTL", "key"])).unwrap(),
2734 Command::Pttl { key: "key".into() },
2735 );
2736 }
2737
2738 #[test]
2739 fn pttl_wrong_arity() {
2740 let err = Command::from_frame(cmd(&["PTTL"])).unwrap_err();
2741 assert!(matches!(err, ProtocolError::WrongArity(_)));
2742 }
2743
2744 #[test]
2747 fn pexpire_basic() {
2748 assert_eq!(
2749 Command::from_frame(cmd(&["PEXPIRE", "key", "5000"])).unwrap(),
2750 Command::Pexpire {
2751 key: "key".into(),
2752 milliseconds: 5000,
2753 },
2754 );
2755 }
2756
2757 #[test]
2758 fn pexpire_wrong_arity() {
2759 let err = Command::from_frame(cmd(&["PEXPIRE", "key"])).unwrap_err();
2760 assert!(matches!(err, ProtocolError::WrongArity(_)));
2761 }
2762
2763 #[test]
2764 fn pexpire_zero_millis() {
2765 let err = Command::from_frame(cmd(&["PEXPIRE", "key", "0"])).unwrap_err();
2766 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2767 }
2768
2769 #[test]
2770 fn pexpire_invalid_millis() {
2771 let err = Command::from_frame(cmd(&["PEXPIRE", "key", "notanum"])).unwrap_err();
2772 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2773 }
2774
2775 #[test]
2778 fn scan_basic() {
2779 assert_eq!(
2780 Command::from_frame(cmd(&["SCAN", "0"])).unwrap(),
2781 Command::Scan {
2782 cursor: 0,
2783 pattern: None,
2784 count: None,
2785 },
2786 );
2787 }
2788
2789 #[test]
2790 fn scan_with_match() {
2791 assert_eq!(
2792 Command::from_frame(cmd(&["SCAN", "0", "MATCH", "user:*"])).unwrap(),
2793 Command::Scan {
2794 cursor: 0,
2795 pattern: Some("user:*".into()),
2796 count: None,
2797 },
2798 );
2799 }
2800
2801 #[test]
2802 fn scan_with_count() {
2803 assert_eq!(
2804 Command::from_frame(cmd(&["SCAN", "42", "COUNT", "100"])).unwrap(),
2805 Command::Scan {
2806 cursor: 42,
2807 pattern: None,
2808 count: Some(100),
2809 },
2810 );
2811 }
2812
2813 #[test]
2814 fn scan_with_match_and_count() {
2815 assert_eq!(
2816 Command::from_frame(cmd(&["SCAN", "0", "MATCH", "*:data", "COUNT", "50"])).unwrap(),
2817 Command::Scan {
2818 cursor: 0,
2819 pattern: Some("*:data".into()),
2820 count: Some(50),
2821 },
2822 );
2823 }
2824
2825 #[test]
2826 fn scan_count_before_match() {
2827 assert_eq!(
2828 Command::from_frame(cmd(&["SCAN", "0", "COUNT", "10", "MATCH", "foo*"])).unwrap(),
2829 Command::Scan {
2830 cursor: 0,
2831 pattern: Some("foo*".into()),
2832 count: Some(10),
2833 },
2834 );
2835 }
2836
2837 #[test]
2838 fn scan_case_insensitive() {
2839 assert_eq!(
2840 Command::from_frame(cmd(&["scan", "0", "match", "x*", "count", "5"])).unwrap(),
2841 Command::Scan {
2842 cursor: 0,
2843 pattern: Some("x*".into()),
2844 count: Some(5),
2845 },
2846 );
2847 }
2848
2849 #[test]
2850 fn scan_wrong_arity() {
2851 let err = Command::from_frame(cmd(&["SCAN"])).unwrap_err();
2852 assert!(matches!(err, ProtocolError::WrongArity(_)));
2853 }
2854
2855 #[test]
2856 fn scan_invalid_cursor() {
2857 let err = Command::from_frame(cmd(&["SCAN", "notanum"])).unwrap_err();
2858 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2859 }
2860
2861 #[test]
2862 fn scan_invalid_count() {
2863 let err = Command::from_frame(cmd(&["SCAN", "0", "COUNT", "bad"])).unwrap_err();
2864 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2865 }
2866
2867 #[test]
2868 fn scan_unknown_flag() {
2869 let err = Command::from_frame(cmd(&["SCAN", "0", "BADOPT", "val"])).unwrap_err();
2870 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2871 }
2872
2873 #[test]
2874 fn scan_match_missing_pattern() {
2875 let err = Command::from_frame(cmd(&["SCAN", "0", "MATCH"])).unwrap_err();
2876 assert!(matches!(err, ProtocolError::WrongArity(_)));
2877 }
2878
2879 #[test]
2880 fn scan_count_missing_value() {
2881 let err = Command::from_frame(cmd(&["SCAN", "0", "COUNT"])).unwrap_err();
2882 assert!(matches!(err, ProtocolError::WrongArity(_)));
2883 }
2884
2885 #[test]
2888 fn hset_single_field() {
2889 assert_eq!(
2890 Command::from_frame(cmd(&["HSET", "h", "field", "value"])).unwrap(),
2891 Command::HSet {
2892 key: "h".into(),
2893 fields: vec![("field".into(), Bytes::from("value"))],
2894 },
2895 );
2896 }
2897
2898 #[test]
2899 fn hset_multiple_fields() {
2900 let parsed = Command::from_frame(cmd(&["HSET", "h", "f1", "v1", "f2", "v2"])).unwrap();
2901 match parsed {
2902 Command::HSet { key, fields } => {
2903 assert_eq!(key, "h");
2904 assert_eq!(fields.len(), 2);
2905 }
2906 other => panic!("expected HSet, got {other:?}"),
2907 }
2908 }
2909
2910 #[test]
2911 fn hset_wrong_arity() {
2912 let err = Command::from_frame(cmd(&["HSET", "h"])).unwrap_err();
2913 assert!(matches!(err, ProtocolError::WrongArity(_)));
2914 let err = Command::from_frame(cmd(&["HSET", "h", "f"])).unwrap_err();
2915 assert!(matches!(err, ProtocolError::WrongArity(_)));
2916 }
2917
2918 #[test]
2919 fn hget_basic() {
2920 assert_eq!(
2921 Command::from_frame(cmd(&["HGET", "h", "field"])).unwrap(),
2922 Command::HGet {
2923 key: "h".into(),
2924 field: "field".into(),
2925 },
2926 );
2927 }
2928
2929 #[test]
2930 fn hget_wrong_arity() {
2931 let err = Command::from_frame(cmd(&["HGET", "h"])).unwrap_err();
2932 assert!(matches!(err, ProtocolError::WrongArity(_)));
2933 }
2934
2935 #[test]
2936 fn hgetall_basic() {
2937 assert_eq!(
2938 Command::from_frame(cmd(&["HGETALL", "h"])).unwrap(),
2939 Command::HGetAll { key: "h".into() },
2940 );
2941 }
2942
2943 #[test]
2944 fn hgetall_wrong_arity() {
2945 let err = Command::from_frame(cmd(&["HGETALL"])).unwrap_err();
2946 assert!(matches!(err, ProtocolError::WrongArity(_)));
2947 }
2948
2949 #[test]
2950 fn hdel_single() {
2951 assert_eq!(
2952 Command::from_frame(cmd(&["HDEL", "h", "f"])).unwrap(),
2953 Command::HDel {
2954 key: "h".into(),
2955 fields: vec!["f".into()],
2956 },
2957 );
2958 }
2959
2960 #[test]
2961 fn hdel_multiple() {
2962 let parsed = Command::from_frame(cmd(&["HDEL", "h", "f1", "f2", "f3"])).unwrap();
2963 match parsed {
2964 Command::HDel { fields, .. } => assert_eq!(fields.len(), 3),
2965 other => panic!("expected HDel, got {other:?}"),
2966 }
2967 }
2968
2969 #[test]
2970 fn hdel_wrong_arity() {
2971 let err = Command::from_frame(cmd(&["HDEL", "h"])).unwrap_err();
2972 assert!(matches!(err, ProtocolError::WrongArity(_)));
2973 }
2974
2975 #[test]
2976 fn hexists_basic() {
2977 assert_eq!(
2978 Command::from_frame(cmd(&["HEXISTS", "h", "f"])).unwrap(),
2979 Command::HExists {
2980 key: "h".into(),
2981 field: "f".into(),
2982 },
2983 );
2984 }
2985
2986 #[test]
2987 fn hlen_basic() {
2988 assert_eq!(
2989 Command::from_frame(cmd(&["HLEN", "h"])).unwrap(),
2990 Command::HLen { key: "h".into() },
2991 );
2992 }
2993
2994 #[test]
2995 fn hincrby_basic() {
2996 assert_eq!(
2997 Command::from_frame(cmd(&["HINCRBY", "h", "f", "5"])).unwrap(),
2998 Command::HIncrBy {
2999 key: "h".into(),
3000 field: "f".into(),
3001 delta: 5,
3002 },
3003 );
3004 }
3005
3006 #[test]
3007 fn hincrby_negative() {
3008 assert_eq!(
3009 Command::from_frame(cmd(&["HINCRBY", "h", "f", "-3"])).unwrap(),
3010 Command::HIncrBy {
3011 key: "h".into(),
3012 field: "f".into(),
3013 delta: -3,
3014 },
3015 );
3016 }
3017
3018 #[test]
3019 fn hincrby_wrong_arity() {
3020 let err = Command::from_frame(cmd(&["HINCRBY", "h", "f"])).unwrap_err();
3021 assert!(matches!(err, ProtocolError::WrongArity(_)));
3022 }
3023
3024 #[test]
3025 fn hkeys_basic() {
3026 assert_eq!(
3027 Command::from_frame(cmd(&["HKEYS", "h"])).unwrap(),
3028 Command::HKeys { key: "h".into() },
3029 );
3030 }
3031
3032 #[test]
3033 fn hvals_basic() {
3034 assert_eq!(
3035 Command::from_frame(cmd(&["HVALS", "h"])).unwrap(),
3036 Command::HVals { key: "h".into() },
3037 );
3038 }
3039
3040 #[test]
3041 fn hmget_basic() {
3042 assert_eq!(
3043 Command::from_frame(cmd(&["HMGET", "h", "f1", "f2"])).unwrap(),
3044 Command::HMGet {
3045 key: "h".into(),
3046 fields: vec!["f1".into(), "f2".into()],
3047 },
3048 );
3049 }
3050
3051 #[test]
3052 fn hmget_wrong_arity() {
3053 let err = Command::from_frame(cmd(&["HMGET", "h"])).unwrap_err();
3054 assert!(matches!(err, ProtocolError::WrongArity(_)));
3055 }
3056
3057 #[test]
3058 fn hash_commands_case_insensitive() {
3059 assert!(matches!(
3060 Command::from_frame(cmd(&["hset", "h", "f", "v"])).unwrap(),
3061 Command::HSet { .. }
3062 ));
3063 assert!(matches!(
3064 Command::from_frame(cmd(&["hget", "h", "f"])).unwrap(),
3065 Command::HGet { .. }
3066 ));
3067 assert!(matches!(
3068 Command::from_frame(cmd(&["hgetall", "h"])).unwrap(),
3069 Command::HGetAll { .. }
3070 ));
3071 }
3072
3073 #[test]
3076 fn sadd_single_member() {
3077 assert_eq!(
3078 Command::from_frame(cmd(&["SADD", "s", "member"])).unwrap(),
3079 Command::SAdd {
3080 key: "s".into(),
3081 members: vec!["member".into()],
3082 },
3083 );
3084 }
3085
3086 #[test]
3087 fn sadd_multiple_members() {
3088 let parsed = Command::from_frame(cmd(&["SADD", "s", "a", "b", "c"])).unwrap();
3089 match parsed {
3090 Command::SAdd { key, members } => {
3091 assert_eq!(key, "s");
3092 assert_eq!(members.len(), 3);
3093 }
3094 other => panic!("expected SAdd, got {other:?}"),
3095 }
3096 }
3097
3098 #[test]
3099 fn sadd_wrong_arity() {
3100 let err = Command::from_frame(cmd(&["SADD", "s"])).unwrap_err();
3101 assert!(matches!(err, ProtocolError::WrongArity(_)));
3102 }
3103
3104 #[test]
3105 fn srem_single_member() {
3106 assert_eq!(
3107 Command::from_frame(cmd(&["SREM", "s", "member"])).unwrap(),
3108 Command::SRem {
3109 key: "s".into(),
3110 members: vec!["member".into()],
3111 },
3112 );
3113 }
3114
3115 #[test]
3116 fn srem_multiple_members() {
3117 let parsed = Command::from_frame(cmd(&["SREM", "s", "a", "b"])).unwrap();
3118 match parsed {
3119 Command::SRem { key, members } => {
3120 assert_eq!(key, "s");
3121 assert_eq!(members.len(), 2);
3122 }
3123 other => panic!("expected SRem, got {other:?}"),
3124 }
3125 }
3126
3127 #[test]
3128 fn srem_wrong_arity() {
3129 let err = Command::from_frame(cmd(&["SREM", "s"])).unwrap_err();
3130 assert!(matches!(err, ProtocolError::WrongArity(_)));
3131 }
3132
3133 #[test]
3134 fn smembers_basic() {
3135 assert_eq!(
3136 Command::from_frame(cmd(&["SMEMBERS", "s"])).unwrap(),
3137 Command::SMembers { key: "s".into() },
3138 );
3139 }
3140
3141 #[test]
3142 fn smembers_wrong_arity() {
3143 let err = Command::from_frame(cmd(&["SMEMBERS"])).unwrap_err();
3144 assert!(matches!(err, ProtocolError::WrongArity(_)));
3145 }
3146
3147 #[test]
3148 fn sismember_basic() {
3149 assert_eq!(
3150 Command::from_frame(cmd(&["SISMEMBER", "s", "member"])).unwrap(),
3151 Command::SIsMember {
3152 key: "s".into(),
3153 member: "member".into(),
3154 },
3155 );
3156 }
3157
3158 #[test]
3159 fn sismember_wrong_arity() {
3160 let err = Command::from_frame(cmd(&["SISMEMBER", "s"])).unwrap_err();
3161 assert!(matches!(err, ProtocolError::WrongArity(_)));
3162 }
3163
3164 #[test]
3165 fn scard_basic() {
3166 assert_eq!(
3167 Command::from_frame(cmd(&["SCARD", "s"])).unwrap(),
3168 Command::SCard { key: "s".into() },
3169 );
3170 }
3171
3172 #[test]
3173 fn scard_wrong_arity() {
3174 let err = Command::from_frame(cmd(&["SCARD"])).unwrap_err();
3175 assert!(matches!(err, ProtocolError::WrongArity(_)));
3176 }
3177
3178 #[test]
3179 fn set_commands_case_insensitive() {
3180 assert!(matches!(
3181 Command::from_frame(cmd(&["sadd", "s", "m"])).unwrap(),
3182 Command::SAdd { .. }
3183 ));
3184 assert!(matches!(
3185 Command::from_frame(cmd(&["srem", "s", "m"])).unwrap(),
3186 Command::SRem { .. }
3187 ));
3188 assert!(matches!(
3189 Command::from_frame(cmd(&["smembers", "s"])).unwrap(),
3190 Command::SMembers { .. }
3191 ));
3192 }
3193
3194 #[test]
3197 fn cluster_info_basic() {
3198 assert_eq!(
3199 Command::from_frame(cmd(&["CLUSTER", "INFO"])).unwrap(),
3200 Command::ClusterInfo,
3201 );
3202 }
3203
3204 #[test]
3205 fn cluster_nodes_basic() {
3206 assert_eq!(
3207 Command::from_frame(cmd(&["CLUSTER", "NODES"])).unwrap(),
3208 Command::ClusterNodes,
3209 );
3210 }
3211
3212 #[test]
3213 fn cluster_slots_basic() {
3214 assert_eq!(
3215 Command::from_frame(cmd(&["CLUSTER", "SLOTS"])).unwrap(),
3216 Command::ClusterSlots,
3217 );
3218 }
3219
3220 #[test]
3221 fn cluster_keyslot_basic() {
3222 assert_eq!(
3223 Command::from_frame(cmd(&["CLUSTER", "KEYSLOT", "mykey"])).unwrap(),
3224 Command::ClusterKeySlot {
3225 key: "mykey".into()
3226 },
3227 );
3228 }
3229
3230 #[test]
3231 fn cluster_keyslot_wrong_arity() {
3232 let err = Command::from_frame(cmd(&["CLUSTER", "KEYSLOT"])).unwrap_err();
3233 assert!(matches!(err, ProtocolError::WrongArity(_)));
3234 }
3235
3236 #[test]
3237 fn cluster_myid_basic() {
3238 assert_eq!(
3239 Command::from_frame(cmd(&["CLUSTER", "MYID"])).unwrap(),
3240 Command::ClusterMyId,
3241 );
3242 }
3243
3244 #[test]
3245 fn cluster_unknown_subcommand() {
3246 let err = Command::from_frame(cmd(&["CLUSTER", "BADCMD"])).unwrap_err();
3247 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3248 }
3249
3250 #[test]
3251 fn cluster_no_subcommand() {
3252 let err = Command::from_frame(cmd(&["CLUSTER"])).unwrap_err();
3253 assert!(matches!(err, ProtocolError::WrongArity(_)));
3254 }
3255
3256 #[test]
3257 fn cluster_case_insensitive() {
3258 assert!(matches!(
3259 Command::from_frame(cmd(&["cluster", "info"])).unwrap(),
3260 Command::ClusterInfo
3261 ));
3262 assert!(matches!(
3263 Command::from_frame(cmd(&["cluster", "keyslot", "k"])).unwrap(),
3264 Command::ClusterKeySlot { .. }
3265 ));
3266 }
3267
3268 #[test]
3269 fn asking_basic() {
3270 assert_eq!(
3271 Command::from_frame(cmd(&["ASKING"])).unwrap(),
3272 Command::Asking,
3273 );
3274 }
3275
3276 #[test]
3277 fn asking_wrong_arity() {
3278 let err = Command::from_frame(cmd(&["ASKING", "extra"])).unwrap_err();
3279 assert!(matches!(err, ProtocolError::WrongArity(_)));
3280 }
3281
3282 #[test]
3283 fn cluster_setslot_importing() {
3284 assert_eq!(
3285 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "100", "IMPORTING", "node123"]))
3286 .unwrap(),
3287 Command::ClusterSetSlotImporting {
3288 slot: 100,
3289 node_id: "node123".into()
3290 },
3291 );
3292 }
3293
3294 #[test]
3295 fn cluster_setslot_migrating() {
3296 assert_eq!(
3297 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "200", "MIGRATING", "node456"]))
3298 .unwrap(),
3299 Command::ClusterSetSlotMigrating {
3300 slot: 200,
3301 node_id: "node456".into()
3302 },
3303 );
3304 }
3305
3306 #[test]
3307 fn cluster_setslot_node() {
3308 assert_eq!(
3309 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "300", "NODE", "node789"])).unwrap(),
3310 Command::ClusterSetSlotNode {
3311 slot: 300,
3312 node_id: "node789".into()
3313 },
3314 );
3315 }
3316
3317 #[test]
3318 fn cluster_setslot_stable() {
3319 assert_eq!(
3320 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "400", "STABLE"])).unwrap(),
3321 Command::ClusterSetSlotStable { slot: 400 },
3322 );
3323 }
3324
3325 #[test]
3326 fn cluster_setslot_invalid_slot() {
3327 let err =
3328 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "notanumber", "STABLE"])).unwrap_err();
3329 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3330 }
3331
3332 #[test]
3333 fn cluster_setslot_wrong_arity() {
3334 let err = Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "100"])).unwrap_err();
3335 assert!(matches!(err, ProtocolError::WrongArity(_)));
3336 }
3337
3338 #[test]
3339 fn migrate_basic() {
3340 assert_eq!(
3341 Command::from_frame(cmd(&["MIGRATE", "127.0.0.1", "6379", "mykey", "0", "5000"]))
3342 .unwrap(),
3343 Command::Migrate {
3344 host: "127.0.0.1".into(),
3345 port: 6379,
3346 key: "mykey".into(),
3347 db: 0,
3348 timeout_ms: 5000,
3349 copy: false,
3350 replace: false,
3351 },
3352 );
3353 }
3354
3355 #[test]
3356 fn migrate_with_options() {
3357 assert_eq!(
3358 Command::from_frame(cmd(&[
3359 "MIGRATE",
3360 "192.168.1.1",
3361 "6380",
3362 "testkey",
3363 "1",
3364 "10000",
3365 "COPY",
3366 "REPLACE"
3367 ]))
3368 .unwrap(),
3369 Command::Migrate {
3370 host: "192.168.1.1".into(),
3371 port: 6380,
3372 key: "testkey".into(),
3373 db: 1,
3374 timeout_ms: 10000,
3375 copy: true,
3376 replace: true,
3377 },
3378 );
3379 }
3380
3381 #[test]
3382 fn migrate_wrong_arity() {
3383 let err = Command::from_frame(cmd(&["MIGRATE", "host", "port", "key"])).unwrap_err();
3384 assert!(matches!(err, ProtocolError::WrongArity(_)));
3385 }
3386
3387 #[test]
3388 fn migrate_invalid_port() {
3389 let err = Command::from_frame(cmd(&["MIGRATE", "host", "notaport", "key", "0", "1000"]))
3390 .unwrap_err();
3391 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3392 }
3393
3394 #[test]
3395 fn cluster_meet_basic() {
3396 assert_eq!(
3397 Command::from_frame(cmd(&["CLUSTER", "MEET", "192.168.1.1", "6379"])).unwrap(),
3398 Command::ClusterMeet {
3399 ip: "192.168.1.1".into(),
3400 port: 6379
3401 },
3402 );
3403 }
3404
3405 #[test]
3406 fn cluster_addslots_basic() {
3407 assert_eq!(
3408 Command::from_frame(cmd(&["CLUSTER", "ADDSLOTS", "0", "1", "2"])).unwrap(),
3409 Command::ClusterAddSlots {
3410 slots: vec![0, 1, 2]
3411 },
3412 );
3413 }
3414
3415 #[test]
3416 fn cluster_delslots_basic() {
3417 assert_eq!(
3418 Command::from_frame(cmd(&["CLUSTER", "DELSLOTS", "100", "101"])).unwrap(),
3419 Command::ClusterDelSlots {
3420 slots: vec![100, 101]
3421 },
3422 );
3423 }
3424
3425 #[test]
3426 fn cluster_forget_basic() {
3427 assert_eq!(
3428 Command::from_frame(cmd(&["CLUSTER", "FORGET", "abc123"])).unwrap(),
3429 Command::ClusterForget {
3430 node_id: "abc123".into()
3431 },
3432 );
3433 }
3434
3435 #[test]
3436 fn cluster_replicate_basic() {
3437 assert_eq!(
3438 Command::from_frame(cmd(&["CLUSTER", "REPLICATE", "master-id"])).unwrap(),
3439 Command::ClusterReplicate {
3440 node_id: "master-id".into()
3441 },
3442 );
3443 }
3444
3445 #[test]
3446 fn cluster_failover_basic() {
3447 assert_eq!(
3448 Command::from_frame(cmd(&["CLUSTER", "FAILOVER"])).unwrap(),
3449 Command::ClusterFailover {
3450 force: false,
3451 takeover: false
3452 },
3453 );
3454 }
3455
3456 #[test]
3457 fn cluster_failover_force() {
3458 assert_eq!(
3459 Command::from_frame(cmd(&["CLUSTER", "FAILOVER", "FORCE"])).unwrap(),
3460 Command::ClusterFailover {
3461 force: true,
3462 takeover: false
3463 },
3464 );
3465 }
3466
3467 #[test]
3468 fn cluster_failover_takeover() {
3469 assert_eq!(
3470 Command::from_frame(cmd(&["CLUSTER", "FAILOVER", "TAKEOVER"])).unwrap(),
3471 Command::ClusterFailover {
3472 force: false,
3473 takeover: true
3474 },
3475 );
3476 }
3477
3478 #[test]
3479 fn cluster_countkeysinslot_basic() {
3480 assert_eq!(
3481 Command::from_frame(cmd(&["CLUSTER", "COUNTKEYSINSLOT", "100"])).unwrap(),
3482 Command::ClusterCountKeysInSlot { slot: 100 },
3483 );
3484 }
3485
3486 #[test]
3487 fn cluster_getkeysinslot_basic() {
3488 assert_eq!(
3489 Command::from_frame(cmd(&["CLUSTER", "GETKEYSINSLOT", "200", "10"])).unwrap(),
3490 Command::ClusterGetKeysInSlot {
3491 slot: 200,
3492 count: 10
3493 },
3494 );
3495 }
3496
3497 #[test]
3500 fn subscribe_single_channel() {
3501 assert_eq!(
3502 Command::from_frame(cmd(&["SUBSCRIBE", "news"])).unwrap(),
3503 Command::Subscribe {
3504 channels: vec!["news".into()]
3505 },
3506 );
3507 }
3508
3509 #[test]
3510 fn subscribe_multiple_channels() {
3511 assert_eq!(
3512 Command::from_frame(cmd(&["SUBSCRIBE", "ch1", "ch2", "ch3"])).unwrap(),
3513 Command::Subscribe {
3514 channels: vec!["ch1".into(), "ch2".into(), "ch3".into()]
3515 },
3516 );
3517 }
3518
3519 #[test]
3520 fn subscribe_no_args() {
3521 let err = Command::from_frame(cmd(&["SUBSCRIBE"])).unwrap_err();
3522 assert!(matches!(err, ProtocolError::WrongArity(_)));
3523 }
3524
3525 #[test]
3526 fn unsubscribe_all() {
3527 assert_eq!(
3528 Command::from_frame(cmd(&["UNSUBSCRIBE"])).unwrap(),
3529 Command::Unsubscribe { channels: vec![] },
3530 );
3531 }
3532
3533 #[test]
3534 fn unsubscribe_specific() {
3535 assert_eq!(
3536 Command::from_frame(cmd(&["UNSUBSCRIBE", "news"])).unwrap(),
3537 Command::Unsubscribe {
3538 channels: vec!["news".into()]
3539 },
3540 );
3541 }
3542
3543 #[test]
3544 fn psubscribe_pattern() {
3545 assert_eq!(
3546 Command::from_frame(cmd(&["PSUBSCRIBE", "news.*"])).unwrap(),
3547 Command::PSubscribe {
3548 patterns: vec!["news.*".into()]
3549 },
3550 );
3551 }
3552
3553 #[test]
3554 fn psubscribe_no_args() {
3555 let err = Command::from_frame(cmd(&["PSUBSCRIBE"])).unwrap_err();
3556 assert!(matches!(err, ProtocolError::WrongArity(_)));
3557 }
3558
3559 #[test]
3560 fn punsubscribe_all() {
3561 assert_eq!(
3562 Command::from_frame(cmd(&["PUNSUBSCRIBE"])).unwrap(),
3563 Command::PUnsubscribe { patterns: vec![] },
3564 );
3565 }
3566
3567 #[test]
3568 fn publish_basic() {
3569 assert_eq!(
3570 Command::from_frame(cmd(&["PUBLISH", "news", "hello world"])).unwrap(),
3571 Command::Publish {
3572 channel: "news".into(),
3573 message: Bytes::from("hello world"),
3574 },
3575 );
3576 }
3577
3578 #[test]
3579 fn publish_wrong_arity() {
3580 let err = Command::from_frame(cmd(&["PUBLISH", "news"])).unwrap_err();
3581 assert!(matches!(err, ProtocolError::WrongArity(_)));
3582 }
3583
3584 #[test]
3585 fn subscribe_case_insensitive() {
3586 assert_eq!(
3587 Command::from_frame(cmd(&["subscribe", "ch"])).unwrap(),
3588 Command::Subscribe {
3589 channels: vec!["ch".into()]
3590 },
3591 );
3592 }
3593
3594 #[test]
3595 fn pubsub_channels_no_pattern() {
3596 assert_eq!(
3597 Command::from_frame(cmd(&["PUBSUB", "CHANNELS"])).unwrap(),
3598 Command::PubSubChannels { pattern: None },
3599 );
3600 }
3601
3602 #[test]
3603 fn pubsub_channels_with_pattern() {
3604 assert_eq!(
3605 Command::from_frame(cmd(&["PUBSUB", "CHANNELS", "news.*"])).unwrap(),
3606 Command::PubSubChannels {
3607 pattern: Some("news.*".into())
3608 },
3609 );
3610 }
3611
3612 #[test]
3613 fn pubsub_numsub_no_args() {
3614 assert_eq!(
3615 Command::from_frame(cmd(&["PUBSUB", "NUMSUB"])).unwrap(),
3616 Command::PubSubNumSub { channels: vec![] },
3617 );
3618 }
3619
3620 #[test]
3621 fn pubsub_numsub_with_channels() {
3622 assert_eq!(
3623 Command::from_frame(cmd(&["PUBSUB", "NUMSUB", "ch1", "ch2"])).unwrap(),
3624 Command::PubSubNumSub {
3625 channels: vec!["ch1".into(), "ch2".into()]
3626 },
3627 );
3628 }
3629
3630 #[test]
3631 fn pubsub_numpat() {
3632 assert_eq!(
3633 Command::from_frame(cmd(&["PUBSUB", "NUMPAT"])).unwrap(),
3634 Command::PubSubNumPat,
3635 );
3636 }
3637
3638 #[test]
3639 fn pubsub_no_subcommand() {
3640 let err = Command::from_frame(cmd(&["PUBSUB"])).unwrap_err();
3641 assert!(matches!(err, ProtocolError::WrongArity(_)));
3642 }
3643
3644 #[test]
3645 fn pubsub_unknown_subcommand() {
3646 let err = Command::from_frame(cmd(&["PUBSUB", "BOGUS"])).unwrap_err();
3647 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3648 }
3649
3650 #[test]
3653 fn incrby_basic() {
3654 assert_eq!(
3655 Command::from_frame(cmd(&["INCRBY", "counter", "5"])).unwrap(),
3656 Command::IncrBy {
3657 key: "counter".into(),
3658 delta: 5
3659 },
3660 );
3661 }
3662
3663 #[test]
3664 fn incrby_negative() {
3665 assert_eq!(
3666 Command::from_frame(cmd(&["INCRBY", "counter", "-3"])).unwrap(),
3667 Command::IncrBy {
3668 key: "counter".into(),
3669 delta: -3
3670 },
3671 );
3672 }
3673
3674 #[test]
3675 fn incrby_wrong_arity() {
3676 let err = Command::from_frame(cmd(&["INCRBY", "key"])).unwrap_err();
3677 assert!(matches!(err, ProtocolError::WrongArity(_)));
3678 }
3679
3680 #[test]
3681 fn incrby_not_integer() {
3682 let err = Command::from_frame(cmd(&["INCRBY", "key", "abc"])).unwrap_err();
3683 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3684 }
3685
3686 #[test]
3687 fn decrby_basic() {
3688 assert_eq!(
3689 Command::from_frame(cmd(&["DECRBY", "counter", "10"])).unwrap(),
3690 Command::DecrBy {
3691 key: "counter".into(),
3692 delta: 10
3693 },
3694 );
3695 }
3696
3697 #[test]
3698 fn decrby_wrong_arity() {
3699 let err = Command::from_frame(cmd(&["DECRBY"])).unwrap_err();
3700 assert!(matches!(err, ProtocolError::WrongArity(_)));
3701 }
3702
3703 #[test]
3706 fn incrbyfloat_basic() {
3707 let cmd = Command::from_frame(cmd(&["INCRBYFLOAT", "key", "2.5"])).unwrap();
3708 match cmd {
3709 Command::IncrByFloat { key, delta } => {
3710 assert_eq!(key, "key");
3711 assert!((delta - 2.5).abs() < f64::EPSILON);
3712 }
3713 other => panic!("expected IncrByFloat, got {other:?}"),
3714 }
3715 }
3716
3717 #[test]
3718 fn incrbyfloat_negative() {
3719 let cmd = Command::from_frame(cmd(&["INCRBYFLOAT", "key", "-1.5"])).unwrap();
3720 match cmd {
3721 Command::IncrByFloat { key, delta } => {
3722 assert_eq!(key, "key");
3723 assert!((delta - (-1.5)).abs() < f64::EPSILON);
3724 }
3725 other => panic!("expected IncrByFloat, got {other:?}"),
3726 }
3727 }
3728
3729 #[test]
3730 fn incrbyfloat_wrong_arity() {
3731 let err = Command::from_frame(cmd(&["INCRBYFLOAT", "key"])).unwrap_err();
3732 assert!(matches!(err, ProtocolError::WrongArity(_)));
3733 }
3734
3735 #[test]
3736 fn incrbyfloat_not_a_float() {
3737 let err = Command::from_frame(cmd(&["INCRBYFLOAT", "key", "abc"])).unwrap_err();
3738 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3739 }
3740
3741 #[test]
3744 fn append_basic() {
3745 assert_eq!(
3746 Command::from_frame(cmd(&["APPEND", "key", "value"])).unwrap(),
3747 Command::Append {
3748 key: "key".into(),
3749 value: Bytes::from("value")
3750 },
3751 );
3752 }
3753
3754 #[test]
3755 fn append_wrong_arity() {
3756 let err = Command::from_frame(cmd(&["APPEND", "key"])).unwrap_err();
3757 assert!(matches!(err, ProtocolError::WrongArity(_)));
3758 }
3759
3760 #[test]
3761 fn strlen_basic() {
3762 assert_eq!(
3763 Command::from_frame(cmd(&["STRLEN", "key"])).unwrap(),
3764 Command::Strlen { key: "key".into() },
3765 );
3766 }
3767
3768 #[test]
3769 fn strlen_wrong_arity() {
3770 let err = Command::from_frame(cmd(&["STRLEN"])).unwrap_err();
3771 assert!(matches!(err, ProtocolError::WrongArity(_)));
3772 }
3773
3774 #[test]
3777 fn keys_basic() {
3778 assert_eq!(
3779 Command::from_frame(cmd(&["KEYS", "user:*"])).unwrap(),
3780 Command::Keys {
3781 pattern: "user:*".into()
3782 },
3783 );
3784 }
3785
3786 #[test]
3787 fn keys_wrong_arity() {
3788 let err = Command::from_frame(cmd(&["KEYS"])).unwrap_err();
3789 assert!(matches!(err, ProtocolError::WrongArity(_)));
3790 }
3791
3792 #[test]
3795 fn rename_basic() {
3796 assert_eq!(
3797 Command::from_frame(cmd(&["RENAME", "old", "new"])).unwrap(),
3798 Command::Rename {
3799 key: "old".into(),
3800 newkey: "new".into()
3801 },
3802 );
3803 }
3804
3805 #[test]
3806 fn rename_wrong_arity() {
3807 let err = Command::from_frame(cmd(&["RENAME", "only"])).unwrap_err();
3808 assert!(matches!(err, ProtocolError::WrongArity(_)));
3809 }
3810
3811 #[test]
3814 fn auth_legacy() {
3815 assert_eq!(
3816 Command::from_frame(cmd(&["AUTH", "secret"])).unwrap(),
3817 Command::Auth {
3818 username: None,
3819 password: "secret".into()
3820 },
3821 );
3822 }
3823
3824 #[test]
3825 fn auth_with_username() {
3826 assert_eq!(
3827 Command::from_frame(cmd(&["AUTH", "default", "secret"])).unwrap(),
3828 Command::Auth {
3829 username: Some("default".into()),
3830 password: "secret".into()
3831 },
3832 );
3833 }
3834
3835 #[test]
3836 fn auth_no_args() {
3837 let err = Command::from_frame(cmd(&["AUTH"])).unwrap_err();
3838 assert!(matches!(err, ProtocolError::WrongArity(_)));
3839 }
3840
3841 #[test]
3842 fn auth_too_many_args() {
3843 let err = Command::from_frame(cmd(&["AUTH", "a", "b", "c"])).unwrap_err();
3844 assert!(matches!(err, ProtocolError::WrongArity(_)));
3845 }
3846
3847 #[test]
3850 fn quit_basic() {
3851 assert_eq!(Command::from_frame(cmd(&["QUIT"])).unwrap(), Command::Quit,);
3852 }
3853
3854 #[test]
3855 fn quit_wrong_arity() {
3856 let err = Command::from_frame(cmd(&["QUIT", "extra"])).unwrap_err();
3857 assert!(matches!(err, ProtocolError::WrongArity(_)));
3858 }
3859}