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