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