1use bytes::Bytes;
8
9use crate::error::ProtocolError;
10use crate::types::Frame;
11
12const MAX_VECTOR_DIMS: usize = 65_536;
16
17const MAX_HNSW_PARAM: u64 = 1024;
20
21const MAX_VSIM_COUNT: u64 = 10_000;
24
25const MAX_VSIM_EF: u64 = MAX_HNSW_PARAM;
28
29const MAX_VADD_BATCH_SIZE: usize = 10_000;
33
34const MAX_SCAN_COUNT: u64 = 10_000_000;
37
38#[derive(Debug, Clone, PartialEq, Eq)]
40pub enum SetExpire {
41 Ex(u64),
43 Px(u64),
45}
46
47#[derive(Debug, Clone, PartialEq)]
49pub enum Command {
50 Ping(Option<Bytes>),
52
53 Echo(Bytes),
55
56 Get { key: String },
58
59 Set {
61 key: String,
62 value: Bytes,
63 expire: Option<SetExpire>,
64 nx: bool,
66 xx: bool,
68 },
69
70 Incr { key: String },
72
73 Decr { key: String },
75
76 IncrBy { key: String, delta: i64 },
78
79 DecrBy { key: String, delta: i64 },
81
82 IncrByFloat { key: String, delta: f64 },
84
85 Append { key: String, value: Bytes },
87
88 Strlen { key: String },
90
91 Keys { pattern: String },
93
94 Rename { key: String, newkey: String },
96
97 Del { keys: Vec<String> },
99
100 Unlink { keys: Vec<String> },
102
103 Exists { keys: Vec<String> },
105
106 MGet { keys: Vec<String> },
108
109 MSet { pairs: Vec<(String, Bytes)> },
111
112 Expire { key: String, seconds: u64 },
114
115 Ttl { key: String },
117
118 Persist { key: String },
120
121 Pttl { key: String },
123
124 Pexpire { key: String, milliseconds: u64 },
126
127 DbSize,
129
130 Info { section: Option<String> },
132
133 BgSave,
135
136 BgRewriteAof,
138
139 FlushDb { async_mode: bool },
141
142 Scan {
144 cursor: u64,
145 pattern: Option<String>,
146 count: Option<usize>,
147 },
148
149 LPush { key: String, values: Vec<Bytes> },
151
152 RPush { key: String, values: Vec<Bytes> },
154
155 LPop { key: String },
157
158 RPop { key: String },
160
161 LRange { key: String, start: i64, stop: i64 },
163
164 LLen { key: String },
166
167 Type { key: String },
169
170 ZAdd {
172 key: String,
173 flags: ZAddFlags,
174 members: Vec<(f64, String)>,
175 },
176
177 ZRem { key: String, members: Vec<String> },
179
180 ZScore { key: String, member: String },
182
183 ZRank { key: String, member: String },
185
186 ZCard { key: String },
188
189 ZRange {
191 key: String,
192 start: i64,
193 stop: i64,
194 with_scores: bool,
195 },
196
197 HSet {
199 key: String,
200 fields: Vec<(String, Bytes)>,
201 },
202
203 HGet { key: String, field: String },
205
206 HGetAll { key: String },
208
209 HDel { key: String, fields: Vec<String> },
211
212 HExists { key: String, field: String },
214
215 HLen { key: String },
217
218 HIncrBy {
220 key: String,
221 field: String,
222 delta: i64,
223 },
224
225 HKeys { key: String },
227
228 HVals { key: String },
230
231 HMGet { key: String, fields: Vec<String> },
233
234 SAdd { key: String, members: Vec<String> },
236
237 SRem { key: String, members: Vec<String> },
239
240 SMembers { key: String },
242
243 SIsMember { key: String, member: String },
245
246 SCard { key: String },
248
249 ClusterInfo,
252
253 ClusterNodes,
255
256 ClusterSlots,
258
259 ClusterKeySlot { key: String },
261
262 ClusterMyId,
264
265 ClusterSetSlotImporting { slot: u16, node_id: String },
267
268 ClusterSetSlotMigrating { slot: u16, node_id: String },
270
271 ClusterSetSlotNode { slot: u16, node_id: String },
273
274 ClusterSetSlotStable { slot: u16 },
276
277 ClusterMeet { ip: String, port: u16 },
279
280 ClusterAddSlots { slots: Vec<u16> },
282
283 ClusterDelSlots { slots: Vec<u16> },
285
286 ClusterForget { node_id: String },
288
289 ClusterReplicate { node_id: String },
291
292 ClusterFailover { force: bool, takeover: bool },
294
295 ClusterCountKeysInSlot { slot: u16 },
297
298 ClusterGetKeysInSlot { slot: u16, count: u32 },
300
301 Migrate {
304 host: String,
305 port: u16,
306 key: String,
307 db: u32,
308 timeout_ms: u64,
309 copy: bool,
310 replace: bool,
311 },
312
313 Asking,
315
316 SlowLogGet { count: Option<usize> },
318
319 SlowLogLen,
321
322 SlowLogReset,
324
325 Subscribe { channels: Vec<String> },
328
329 Unsubscribe { channels: Vec<String> },
331
332 PSubscribe { patterns: Vec<String> },
334
335 PUnsubscribe { patterns: Vec<String> },
337
338 Publish { channel: String, message: Bytes },
340
341 PubSubChannels { pattern: Option<String> },
343
344 PubSubNumSub { channels: Vec<String> },
346
347 PubSubNumPat,
349
350 VAdd {
354 key: String,
355 element: String,
356 vector: Vec<f32>,
357 metric: u8,
359 quantization: u8,
361 connectivity: u32,
363 expansion_add: u32,
365 },
366
367 VAddBatch {
370 key: String,
371 entries: Vec<(String, Vec<f32>)>,
372 dim: usize,
373 metric: u8,
375 quantization: u8,
377 connectivity: u32,
379 expansion_add: u32,
381 },
382
383 VSim {
386 key: String,
387 query: Vec<f32>,
388 count: usize,
389 ef_search: usize,
390 with_scores: bool,
391 },
392
393 VRem { key: String, element: String },
395
396 VGet { key: String, element: String },
398
399 VCard { key: String },
401
402 VDim { key: String },
404
405 VInfo { key: String },
407
408 ProtoRegister { name: String, descriptor: Bytes },
412
413 ProtoSet {
416 key: String,
417 type_name: String,
418 data: Bytes,
419 expire: Option<SetExpire>,
420 nx: bool,
422 xx: bool,
424 },
425
426 ProtoGet { key: String },
428
429 ProtoType { key: String },
431
432 ProtoSchemas,
434
435 ProtoDescribe { name: String },
437
438 ProtoGetField { key: String, field_path: String },
441
442 ProtoSetField {
445 key: String,
446 field_path: String,
447 value: String,
448 },
449
450 ProtoDelField { key: String, field_path: String },
452
453 Auth {
455 username: Option<String>,
457 password: String,
459 },
460
461 Quit,
463
464 Unknown(String),
466}
467
468#[derive(Debug, Clone, Default, PartialEq)]
470pub struct ZAddFlags {
471 pub nx: bool,
473 pub xx: bool,
475 pub gt: bool,
477 pub lt: bool,
479 pub ch: bool,
481}
482
483impl Eq for ZAddFlags {}
484
485impl Command {
486 pub fn command_name(&self) -> &'static str {
491 match self {
492 Command::Ping(_) => "ping",
493 Command::Echo(_) => "echo",
494 Command::Get { .. } => "get",
495 Command::Set { .. } => "set",
496 Command::Incr { .. } => "incr",
497 Command::Decr { .. } => "decr",
498 Command::IncrBy { .. } => "incrby",
499 Command::DecrBy { .. } => "decrby",
500 Command::IncrByFloat { .. } => "incrbyfloat",
501 Command::Append { .. } => "append",
502 Command::Strlen { .. } => "strlen",
503 Command::Keys { .. } => "keys",
504 Command::Rename { .. } => "rename",
505 Command::Del { .. } => "del",
506 Command::Unlink { .. } => "unlink",
507 Command::Exists { .. } => "exists",
508 Command::MGet { .. } => "mget",
509 Command::MSet { .. } => "mset",
510 Command::Expire { .. } => "expire",
511 Command::Ttl { .. } => "ttl",
512 Command::Persist { .. } => "persist",
513 Command::Pttl { .. } => "pttl",
514 Command::Pexpire { .. } => "pexpire",
515 Command::DbSize => "dbsize",
516 Command::Info { .. } => "info",
517 Command::BgSave => "bgsave",
518 Command::BgRewriteAof => "bgrewriteaof",
519 Command::FlushDb { .. } => "flushdb",
520 Command::Scan { .. } => "scan",
521 Command::LPush { .. } => "lpush",
522 Command::RPush { .. } => "rpush",
523 Command::LPop { .. } => "lpop",
524 Command::RPop { .. } => "rpop",
525 Command::LRange { .. } => "lrange",
526 Command::LLen { .. } => "llen",
527 Command::Type { .. } => "type",
528 Command::ZAdd { .. } => "zadd",
529 Command::ZRem { .. } => "zrem",
530 Command::ZScore { .. } => "zscore",
531 Command::ZRank { .. } => "zrank",
532 Command::ZCard { .. } => "zcard",
533 Command::ZRange { .. } => "zrange",
534 Command::HSet { .. } => "hset",
535 Command::HGet { .. } => "hget",
536 Command::HGetAll { .. } => "hgetall",
537 Command::HDel { .. } => "hdel",
538 Command::HExists { .. } => "hexists",
539 Command::HLen { .. } => "hlen",
540 Command::HIncrBy { .. } => "hincrby",
541 Command::HKeys { .. } => "hkeys",
542 Command::HVals { .. } => "hvals",
543 Command::HMGet { .. } => "hmget",
544 Command::SAdd { .. } => "sadd",
545 Command::SRem { .. } => "srem",
546 Command::SMembers { .. } => "smembers",
547 Command::SIsMember { .. } => "sismember",
548 Command::SCard { .. } => "scard",
549 Command::ClusterInfo => "cluster_info",
550 Command::ClusterNodes => "cluster_nodes",
551 Command::ClusterSlots => "cluster_slots",
552 Command::ClusterKeySlot { .. } => "cluster_keyslot",
553 Command::ClusterMyId => "cluster_myid",
554 Command::ClusterSetSlotImporting { .. } => "cluster_setslot",
555 Command::ClusterSetSlotMigrating { .. } => "cluster_setslot",
556 Command::ClusterSetSlotNode { .. } => "cluster_setslot",
557 Command::ClusterSetSlotStable { .. } => "cluster_setslot",
558 Command::ClusterMeet { .. } => "cluster_meet",
559 Command::ClusterAddSlots { .. } => "cluster_addslots",
560 Command::ClusterDelSlots { .. } => "cluster_delslots",
561 Command::ClusterForget { .. } => "cluster_forget",
562 Command::ClusterReplicate { .. } => "cluster_replicate",
563 Command::ClusterFailover { .. } => "cluster_failover",
564 Command::ClusterCountKeysInSlot { .. } => "cluster_countkeysinslot",
565 Command::ClusterGetKeysInSlot { .. } => "cluster_getkeysinslot",
566 Command::Migrate { .. } => "migrate",
567 Command::Asking => "asking",
568 Command::SlowLogGet { .. } => "slowlog",
569 Command::SlowLogLen => "slowlog",
570 Command::SlowLogReset => "slowlog",
571 Command::Subscribe { .. } => "subscribe",
572 Command::Unsubscribe { .. } => "unsubscribe",
573 Command::PSubscribe { .. } => "psubscribe",
574 Command::PUnsubscribe { .. } => "punsubscribe",
575 Command::Publish { .. } => "publish",
576 Command::PubSubChannels { .. } => "pubsub",
577 Command::PubSubNumSub { .. } => "pubsub",
578 Command::PubSubNumPat => "pubsub",
579 Command::VAdd { .. } => "vadd",
580 Command::VAddBatch { .. } => "vadd_batch",
581 Command::VSim { .. } => "vsim",
582 Command::VRem { .. } => "vrem",
583 Command::VGet { .. } => "vget",
584 Command::VCard { .. } => "vcard",
585 Command::VDim { .. } => "vdim",
586 Command::VInfo { .. } => "vinfo",
587 Command::ProtoRegister { .. } => "proto.register",
588 Command::ProtoSet { .. } => "proto.set",
589 Command::ProtoGet { .. } => "proto.get",
590 Command::ProtoType { .. } => "proto.type",
591 Command::ProtoSchemas => "proto.schemas",
592 Command::ProtoDescribe { .. } => "proto.describe",
593 Command::ProtoGetField { .. } => "proto.getfield",
594 Command::ProtoSetField { .. } => "proto.setfield",
595 Command::ProtoDelField { .. } => "proto.delfield",
596 Command::Auth { .. } => "auth",
597 Command::Quit => "quit",
598 Command::Unknown(_) => "unknown",
599 }
600 }
601
602 pub fn from_frame(frame: Frame) -> Result<Command, ProtocolError> {
607 let frames = match frame {
608 Frame::Array(frames) => frames,
609 _ => {
610 return Err(ProtocolError::InvalidCommandFrame(
611 "expected array frame".into(),
612 ));
613 }
614 };
615
616 if frames.is_empty() {
617 return Err(ProtocolError::InvalidCommandFrame(
618 "empty command array".into(),
619 ));
620 }
621
622 let name = extract_string(&frames[0])?;
623 let name_upper = name.to_ascii_uppercase();
624
625 match name_upper.as_str() {
626 "PING" => parse_ping(&frames[1..]),
627 "ECHO" => parse_echo(&frames[1..]),
628 "GET" => parse_get(&frames[1..]),
629 "SET" => parse_set(&frames[1..]),
630 "INCR" => parse_incr(&frames[1..]),
631 "DECR" => parse_decr(&frames[1..]),
632 "INCRBY" => parse_incrby(&frames[1..]),
633 "DECRBY" => parse_decrby(&frames[1..]),
634 "INCRBYFLOAT" => parse_incrbyfloat(&frames[1..]),
635 "APPEND" => parse_append(&frames[1..]),
636 "STRLEN" => parse_strlen(&frames[1..]),
637 "KEYS" => parse_keys(&frames[1..]),
638 "RENAME" => parse_rename(&frames[1..]),
639 "DEL" => parse_del(&frames[1..]),
640 "UNLINK" => parse_unlink(&frames[1..]),
641 "EXISTS" => parse_exists(&frames[1..]),
642 "MGET" => parse_mget(&frames[1..]),
643 "MSET" => parse_mset(&frames[1..]),
644 "EXPIRE" => parse_expire(&frames[1..]),
645 "TTL" => parse_ttl(&frames[1..]),
646 "PERSIST" => parse_persist(&frames[1..]),
647 "PTTL" => parse_pttl(&frames[1..]),
648 "PEXPIRE" => parse_pexpire(&frames[1..]),
649 "DBSIZE" => parse_dbsize(&frames[1..]),
650 "INFO" => parse_info(&frames[1..]),
651 "BGSAVE" => parse_bgsave(&frames[1..]),
652 "BGREWRITEAOF" => parse_bgrewriteaof(&frames[1..]),
653 "FLUSHDB" => parse_flushdb(&frames[1..]),
654 "SCAN" => parse_scan(&frames[1..]),
655 "LPUSH" => parse_lpush(&frames[1..]),
656 "RPUSH" => parse_rpush(&frames[1..]),
657 "LPOP" => parse_lpop(&frames[1..]),
658 "RPOP" => parse_rpop(&frames[1..]),
659 "LRANGE" => parse_lrange(&frames[1..]),
660 "LLEN" => parse_llen(&frames[1..]),
661 "TYPE" => parse_type(&frames[1..]),
662 "ZADD" => parse_zadd(&frames[1..]),
663 "ZREM" => parse_zrem(&frames[1..]),
664 "ZSCORE" => parse_zscore(&frames[1..]),
665 "ZRANK" => parse_zrank(&frames[1..]),
666 "ZCARD" => parse_zcard(&frames[1..]),
667 "ZRANGE" => parse_zrange(&frames[1..]),
668 "HSET" => parse_hset(&frames[1..]),
669 "HGET" => parse_hget(&frames[1..]),
670 "HGETALL" => parse_hgetall(&frames[1..]),
671 "HDEL" => parse_hdel(&frames[1..]),
672 "HEXISTS" => parse_hexists(&frames[1..]),
673 "HLEN" => parse_hlen(&frames[1..]),
674 "HINCRBY" => parse_hincrby(&frames[1..]),
675 "HKEYS" => parse_hkeys(&frames[1..]),
676 "HVALS" => parse_hvals(&frames[1..]),
677 "HMGET" => parse_hmget(&frames[1..]),
678 "SADD" => parse_sadd(&frames[1..]),
679 "SREM" => parse_srem(&frames[1..]),
680 "SMEMBERS" => parse_smembers(&frames[1..]),
681 "SISMEMBER" => parse_sismember(&frames[1..]),
682 "SCARD" => parse_scard(&frames[1..]),
683 "CLUSTER" => parse_cluster(&frames[1..]),
684 "ASKING" => parse_asking(&frames[1..]),
685 "MIGRATE" => parse_migrate(&frames[1..]),
686 "SLOWLOG" => parse_slowlog(&frames[1..]),
687 "SUBSCRIBE" => parse_subscribe(&frames[1..]),
688 "UNSUBSCRIBE" => parse_unsubscribe(&frames[1..]),
689 "PSUBSCRIBE" => parse_psubscribe(&frames[1..]),
690 "PUNSUBSCRIBE" => parse_punsubscribe(&frames[1..]),
691 "PUBLISH" => parse_publish(&frames[1..]),
692 "PUBSUB" => parse_pubsub(&frames[1..]),
693 "VADD" => parse_vadd(&frames[1..]),
694 "VADD_BATCH" => parse_vadd_batch(&frames[1..]),
695 "VSIM" => parse_vsim(&frames[1..]),
696 "VREM" => parse_vrem(&frames[1..]),
697 "VGET" => parse_vget(&frames[1..]),
698 "VCARD" => parse_vcard(&frames[1..]),
699 "VDIM" => parse_vdim(&frames[1..]),
700 "VINFO" => parse_vinfo(&frames[1..]),
701 "PROTO.REGISTER" => parse_proto_register(&frames[1..]),
702 "PROTO.SET" => parse_proto_set(&frames[1..]),
703 "PROTO.GET" => parse_proto_get(&frames[1..]),
704 "PROTO.TYPE" => parse_proto_type(&frames[1..]),
705 "PROTO.SCHEMAS" => parse_proto_schemas(&frames[1..]),
706 "PROTO.DESCRIBE" => parse_proto_describe(&frames[1..]),
707 "PROTO.GETFIELD" => parse_proto_getfield(&frames[1..]),
708 "PROTO.SETFIELD" => parse_proto_setfield(&frames[1..]),
709 "PROTO.DELFIELD" => parse_proto_delfield(&frames[1..]),
710 "AUTH" => parse_auth(&frames[1..]),
711 "QUIT" => parse_quit(&frames[1..]),
712 _ => Ok(Command::Unknown(name)),
713 }
714 }
715}
716
717fn extract_string(frame: &Frame) -> Result<String, ProtocolError> {
722 match frame {
723 Frame::Bulk(data) => {
724 let s = std::str::from_utf8(data).map_err(|_| {
725 ProtocolError::InvalidCommandFrame("command name is not valid utf-8".into())
726 })?;
727 Ok(s.to_owned())
728 }
729 Frame::Simple(s) => Ok(s.clone()),
730 _ => Err(ProtocolError::InvalidCommandFrame(
731 "expected bulk or simple string for command name".into(),
732 )),
733 }
734}
735
736fn extract_bytes(frame: &Frame) -> Result<Bytes, ProtocolError> {
738 match frame {
739 Frame::Bulk(data) => Ok(data.clone()),
740 Frame::Simple(s) => Ok(Bytes::copy_from_slice(s.as_bytes())),
741 _ => Err(ProtocolError::InvalidCommandFrame(
742 "expected bulk or simple string argument".into(),
743 )),
744 }
745}
746
747fn extract_strings(frames: &[Frame]) -> Result<Vec<String>, ProtocolError> {
749 frames.iter().map(extract_string).collect()
750}
751
752fn extract_bytes_vec(frames: &[Frame]) -> Result<Vec<Bytes>, ProtocolError> {
754 frames.iter().map(extract_bytes).collect()
755}
756
757fn parse_u64(frame: &Frame, cmd: &str) -> Result<u64, ProtocolError> {
759 let s = extract_string(frame)?;
760 s.parse::<u64>().map_err(|_| {
761 ProtocolError::InvalidCommandFrame(format!("value is not a valid integer for '{cmd}'"))
762 })
763}
764
765fn parse_ping(args: &[Frame]) -> Result<Command, ProtocolError> {
766 match args.len() {
767 0 => Ok(Command::Ping(None)),
768 1 => {
769 let msg = extract_bytes(&args[0])?;
770 Ok(Command::Ping(Some(msg)))
771 }
772 _ => Err(ProtocolError::WrongArity("PING".into())),
773 }
774}
775
776fn parse_echo(args: &[Frame]) -> Result<Command, ProtocolError> {
777 if args.len() != 1 {
778 return Err(ProtocolError::WrongArity("ECHO".into()));
779 }
780 let msg = extract_bytes(&args[0])?;
781 Ok(Command::Echo(msg))
782}
783
784fn parse_get(args: &[Frame]) -> Result<Command, ProtocolError> {
785 if args.len() != 1 {
786 return Err(ProtocolError::WrongArity("GET".into()));
787 }
788 let key = extract_string(&args[0])?;
789 Ok(Command::Get { key })
790}
791
792fn parse_set(args: &[Frame]) -> Result<Command, ProtocolError> {
793 if args.len() < 2 {
794 return Err(ProtocolError::WrongArity("SET".into()));
795 }
796
797 let key = extract_string(&args[0])?;
798 let value = extract_bytes(&args[1])?;
799
800 let mut expire = None;
801 let mut nx = false;
802 let mut xx = false;
803 let mut idx = 2;
804
805 while idx < args.len() {
806 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
807 match flag.as_str() {
808 "NX" => {
809 nx = true;
810 idx += 1;
811 }
812 "XX" => {
813 xx = true;
814 idx += 1;
815 }
816 "EX" => {
817 idx += 1;
818 if idx >= args.len() {
819 return Err(ProtocolError::WrongArity("SET".into()));
820 }
821 let amount = parse_u64(&args[idx], "SET")?;
822 if amount == 0 {
823 return Err(ProtocolError::InvalidCommandFrame(
824 "invalid expire time in 'SET' command".into(),
825 ));
826 }
827 expire = Some(SetExpire::Ex(amount));
828 idx += 1;
829 }
830 "PX" => {
831 idx += 1;
832 if idx >= args.len() {
833 return Err(ProtocolError::WrongArity("SET".into()));
834 }
835 let amount = parse_u64(&args[idx], "SET")?;
836 if amount == 0 {
837 return Err(ProtocolError::InvalidCommandFrame(
838 "invalid expire time in 'SET' command".into(),
839 ));
840 }
841 expire = Some(SetExpire::Px(amount));
842 idx += 1;
843 }
844 _ => {
845 return Err(ProtocolError::InvalidCommandFrame(format!(
846 "unsupported SET option '{flag}'"
847 )));
848 }
849 }
850 }
851
852 if nx && xx {
853 return Err(ProtocolError::InvalidCommandFrame(
854 "XX and NX options at the same time are not compatible".into(),
855 ));
856 }
857
858 Ok(Command::Set {
859 key,
860 value,
861 expire,
862 nx,
863 xx,
864 })
865}
866
867fn parse_incr(args: &[Frame]) -> Result<Command, ProtocolError> {
868 if args.len() != 1 {
869 return Err(ProtocolError::WrongArity("INCR".into()));
870 }
871 let key = extract_string(&args[0])?;
872 Ok(Command::Incr { key })
873}
874
875fn parse_decr(args: &[Frame]) -> Result<Command, ProtocolError> {
876 if args.len() != 1 {
877 return Err(ProtocolError::WrongArity("DECR".into()));
878 }
879 let key = extract_string(&args[0])?;
880 Ok(Command::Decr { key })
881}
882
883fn parse_incrby(args: &[Frame]) -> Result<Command, ProtocolError> {
884 if args.len() != 2 {
885 return Err(ProtocolError::WrongArity("INCRBY".into()));
886 }
887 let key = extract_string(&args[0])?;
888 let delta = parse_i64(&args[1], "INCRBY")?;
889 Ok(Command::IncrBy { key, delta })
890}
891
892fn parse_decrby(args: &[Frame]) -> Result<Command, ProtocolError> {
893 if args.len() != 2 {
894 return Err(ProtocolError::WrongArity("DECRBY".into()));
895 }
896 let key = extract_string(&args[0])?;
897 let delta = parse_i64(&args[1], "DECRBY")?;
898 Ok(Command::DecrBy { key, delta })
899}
900
901fn parse_incrbyfloat(args: &[Frame]) -> Result<Command, ProtocolError> {
902 if args.len() != 2 {
903 return Err(ProtocolError::WrongArity("INCRBYFLOAT".into()));
904 }
905 let key = extract_string(&args[0])?;
906 let s = extract_string(&args[1])?;
907 let delta: f64 = s.parse().map_err(|_| {
908 ProtocolError::InvalidCommandFrame("value is not a valid float for 'INCRBYFLOAT'".into())
909 })?;
910 if delta.is_nan() || delta.is_infinite() {
911 return Err(ProtocolError::InvalidCommandFrame(
912 "increment would produce NaN or Infinity".into(),
913 ));
914 }
915 Ok(Command::IncrByFloat { key, delta })
916}
917
918fn parse_append(args: &[Frame]) -> Result<Command, ProtocolError> {
919 if args.len() != 2 {
920 return Err(ProtocolError::WrongArity("APPEND".into()));
921 }
922 let key = extract_string(&args[0])?;
923 let value = extract_bytes(&args[1])?;
924 Ok(Command::Append { key, value })
925}
926
927fn parse_strlen(args: &[Frame]) -> Result<Command, ProtocolError> {
928 if args.len() != 1 {
929 return Err(ProtocolError::WrongArity("STRLEN".into()));
930 }
931 let key = extract_string(&args[0])?;
932 Ok(Command::Strlen { key })
933}
934
935fn parse_keys(args: &[Frame]) -> Result<Command, ProtocolError> {
936 if args.len() != 1 {
937 return Err(ProtocolError::WrongArity("KEYS".into()));
938 }
939 let pattern = extract_string(&args[0])?;
940 Ok(Command::Keys { pattern })
941}
942
943fn parse_rename(args: &[Frame]) -> Result<Command, ProtocolError> {
944 if args.len() != 2 {
945 return Err(ProtocolError::WrongArity("RENAME".into()));
946 }
947 let key = extract_string(&args[0])?;
948 let newkey = extract_string(&args[1])?;
949 Ok(Command::Rename { key, newkey })
950}
951
952fn parse_del(args: &[Frame]) -> Result<Command, ProtocolError> {
953 if args.is_empty() {
954 return Err(ProtocolError::WrongArity("DEL".into()));
955 }
956 let keys = extract_strings(args)?;
957 Ok(Command::Del { keys })
958}
959
960fn parse_exists(args: &[Frame]) -> Result<Command, ProtocolError> {
961 if args.is_empty() {
962 return Err(ProtocolError::WrongArity("EXISTS".into()));
963 }
964 let keys = extract_strings(args)?;
965 Ok(Command::Exists { keys })
966}
967
968fn parse_mget(args: &[Frame]) -> Result<Command, ProtocolError> {
969 if args.is_empty() {
970 return Err(ProtocolError::WrongArity("MGET".into()));
971 }
972 let keys = extract_strings(args)?;
973 Ok(Command::MGet { keys })
974}
975
976fn parse_mset(args: &[Frame]) -> Result<Command, ProtocolError> {
977 if args.is_empty() || !args.len().is_multiple_of(2) {
978 return Err(ProtocolError::WrongArity("MSET".into()));
979 }
980 let mut pairs = Vec::with_capacity(args.len() / 2);
981 for chunk in args.chunks(2) {
982 let key = extract_string(&chunk[0])?;
983 let value = extract_bytes(&chunk[1])?;
984 pairs.push((key, value));
985 }
986 Ok(Command::MSet { pairs })
987}
988
989fn parse_expire(args: &[Frame]) -> Result<Command, ProtocolError> {
990 if args.len() != 2 {
991 return Err(ProtocolError::WrongArity("EXPIRE".into()));
992 }
993 let key = extract_string(&args[0])?;
994 let seconds = parse_u64(&args[1], "EXPIRE")?;
995
996 if seconds == 0 {
997 return Err(ProtocolError::InvalidCommandFrame(
998 "invalid expire time in 'EXPIRE' command".into(),
999 ));
1000 }
1001
1002 Ok(Command::Expire { key, seconds })
1003}
1004
1005fn parse_ttl(args: &[Frame]) -> Result<Command, ProtocolError> {
1006 if args.len() != 1 {
1007 return Err(ProtocolError::WrongArity("TTL".into()));
1008 }
1009 let key = extract_string(&args[0])?;
1010 Ok(Command::Ttl { key })
1011}
1012
1013fn parse_persist(args: &[Frame]) -> Result<Command, ProtocolError> {
1014 if args.len() != 1 {
1015 return Err(ProtocolError::WrongArity("PERSIST".into()));
1016 }
1017 let key = extract_string(&args[0])?;
1018 Ok(Command::Persist { key })
1019}
1020
1021fn parse_pttl(args: &[Frame]) -> Result<Command, ProtocolError> {
1022 if args.len() != 1 {
1023 return Err(ProtocolError::WrongArity("PTTL".into()));
1024 }
1025 let key = extract_string(&args[0])?;
1026 Ok(Command::Pttl { key })
1027}
1028
1029fn parse_pexpire(args: &[Frame]) -> Result<Command, ProtocolError> {
1030 if args.len() != 2 {
1031 return Err(ProtocolError::WrongArity("PEXPIRE".into()));
1032 }
1033 let key = extract_string(&args[0])?;
1034 let milliseconds = parse_u64(&args[1], "PEXPIRE")?;
1035
1036 if milliseconds == 0 {
1037 return Err(ProtocolError::InvalidCommandFrame(
1038 "invalid expire time in 'PEXPIRE' command".into(),
1039 ));
1040 }
1041
1042 Ok(Command::Pexpire { key, milliseconds })
1043}
1044
1045fn parse_dbsize(args: &[Frame]) -> Result<Command, ProtocolError> {
1046 if !args.is_empty() {
1047 return Err(ProtocolError::WrongArity("DBSIZE".into()));
1048 }
1049 Ok(Command::DbSize)
1050}
1051
1052fn parse_info(args: &[Frame]) -> Result<Command, ProtocolError> {
1053 match args.len() {
1054 0 => Ok(Command::Info { section: None }),
1055 1 => {
1056 let section = extract_string(&args[0])?;
1057 Ok(Command::Info {
1058 section: Some(section),
1059 })
1060 }
1061 _ => Err(ProtocolError::WrongArity("INFO".into())),
1062 }
1063}
1064
1065fn parse_bgsave(args: &[Frame]) -> Result<Command, ProtocolError> {
1066 if !args.is_empty() {
1067 return Err(ProtocolError::WrongArity("BGSAVE".into()));
1068 }
1069 Ok(Command::BgSave)
1070}
1071
1072fn parse_bgrewriteaof(args: &[Frame]) -> Result<Command, ProtocolError> {
1073 if !args.is_empty() {
1074 return Err(ProtocolError::WrongArity("BGREWRITEAOF".into()));
1075 }
1076 Ok(Command::BgRewriteAof)
1077}
1078
1079fn parse_flushdb(args: &[Frame]) -> Result<Command, ProtocolError> {
1080 if args.is_empty() {
1081 return Ok(Command::FlushDb { async_mode: false });
1082 }
1083 if args.len() == 1 {
1084 let arg = extract_string(&args[0])?;
1085 if arg.eq_ignore_ascii_case("ASYNC") {
1086 return Ok(Command::FlushDb { async_mode: true });
1087 }
1088 }
1089 Err(ProtocolError::WrongArity("FLUSHDB".into()))
1090}
1091
1092fn parse_unlink(args: &[Frame]) -> Result<Command, ProtocolError> {
1093 if args.is_empty() {
1094 return Err(ProtocolError::WrongArity("UNLINK".into()));
1095 }
1096 let keys = extract_strings(args)?;
1097 Ok(Command::Unlink { keys })
1098}
1099
1100fn parse_scan(args: &[Frame]) -> Result<Command, ProtocolError> {
1101 if args.is_empty() {
1102 return Err(ProtocolError::WrongArity("SCAN".into()));
1103 }
1104
1105 let cursor = parse_u64(&args[0], "SCAN")?;
1106 let mut pattern = None;
1107 let mut count = None;
1108 let mut idx = 1;
1109
1110 while idx < args.len() {
1111 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
1112 match flag.as_str() {
1113 "MATCH" => {
1114 idx += 1;
1115 if idx >= args.len() {
1116 return Err(ProtocolError::WrongArity("SCAN".into()));
1117 }
1118 pattern = Some(extract_string(&args[idx])?);
1119 idx += 1;
1120 }
1121 "COUNT" => {
1122 idx += 1;
1123 if idx >= args.len() {
1124 return Err(ProtocolError::WrongArity("SCAN".into()));
1125 }
1126 let n = parse_u64(&args[idx], "SCAN")?;
1127 if n > MAX_SCAN_COUNT {
1128 return Err(ProtocolError::InvalidCommandFrame(format!(
1129 "SCAN COUNT {n} exceeds max {MAX_SCAN_COUNT}"
1130 )));
1131 }
1132 count = Some(n as usize);
1133 idx += 1;
1134 }
1135 _ => {
1136 return Err(ProtocolError::InvalidCommandFrame(format!(
1137 "unsupported SCAN option '{flag}'"
1138 )));
1139 }
1140 }
1141 }
1142
1143 Ok(Command::Scan {
1144 cursor,
1145 pattern,
1146 count,
1147 })
1148}
1149
1150fn parse_i64(frame: &Frame, cmd: &str) -> Result<i64, ProtocolError> {
1152 let s = extract_string(frame)?;
1153 s.parse::<i64>().map_err(|_| {
1154 ProtocolError::InvalidCommandFrame(format!("value is not a valid integer for '{cmd}'"))
1155 })
1156}
1157
1158fn parse_lpush(args: &[Frame]) -> Result<Command, ProtocolError> {
1159 if args.len() < 2 {
1160 return Err(ProtocolError::WrongArity("LPUSH".into()));
1161 }
1162 let key = extract_string(&args[0])?;
1163 let values = extract_bytes_vec(&args[1..])?;
1164 Ok(Command::LPush { key, values })
1165}
1166
1167fn parse_rpush(args: &[Frame]) -> Result<Command, ProtocolError> {
1168 if args.len() < 2 {
1169 return Err(ProtocolError::WrongArity("RPUSH".into()));
1170 }
1171 let key = extract_string(&args[0])?;
1172 let values = extract_bytes_vec(&args[1..])?;
1173 Ok(Command::RPush { key, values })
1174}
1175
1176fn parse_lpop(args: &[Frame]) -> Result<Command, ProtocolError> {
1177 if args.len() != 1 {
1178 return Err(ProtocolError::WrongArity("LPOP".into()));
1179 }
1180 let key = extract_string(&args[0])?;
1181 Ok(Command::LPop { key })
1182}
1183
1184fn parse_rpop(args: &[Frame]) -> Result<Command, ProtocolError> {
1185 if args.len() != 1 {
1186 return Err(ProtocolError::WrongArity("RPOP".into()));
1187 }
1188 let key = extract_string(&args[0])?;
1189 Ok(Command::RPop { key })
1190}
1191
1192fn parse_lrange(args: &[Frame]) -> Result<Command, ProtocolError> {
1193 if args.len() != 3 {
1194 return Err(ProtocolError::WrongArity("LRANGE".into()));
1195 }
1196 let key = extract_string(&args[0])?;
1197 let start = parse_i64(&args[1], "LRANGE")?;
1198 let stop = parse_i64(&args[2], "LRANGE")?;
1199 Ok(Command::LRange { key, start, stop })
1200}
1201
1202fn parse_llen(args: &[Frame]) -> Result<Command, ProtocolError> {
1203 if args.len() != 1 {
1204 return Err(ProtocolError::WrongArity("LLEN".into()));
1205 }
1206 let key = extract_string(&args[0])?;
1207 Ok(Command::LLen { key })
1208}
1209
1210fn parse_type(args: &[Frame]) -> Result<Command, ProtocolError> {
1211 if args.len() != 1 {
1212 return Err(ProtocolError::WrongArity("TYPE".into()));
1213 }
1214 let key = extract_string(&args[0])?;
1215 Ok(Command::Type { key })
1216}
1217
1218fn parse_f64(frame: &Frame, cmd: &str) -> Result<f64, ProtocolError> {
1220 let s = extract_string(frame)?;
1221 let v = s.parse::<f64>().map_err(|_| {
1222 ProtocolError::InvalidCommandFrame(format!("value is not a valid float for '{cmd}'"))
1223 })?;
1224 if v.is_nan() || v.is_infinite() {
1225 return Err(ProtocolError::InvalidCommandFrame(format!(
1226 "value is not a valid finite float for '{cmd}'"
1227 )));
1228 }
1229 Ok(v)
1230}
1231
1232fn parse_zadd(args: &[Frame]) -> Result<Command, ProtocolError> {
1233 if args.len() < 3 {
1235 return Err(ProtocolError::WrongArity("ZADD".into()));
1236 }
1237
1238 let key = extract_string(&args[0])?;
1239 let mut flags = ZAddFlags::default();
1240 let mut idx = 1;
1241
1242 while idx < args.len() {
1244 let s = extract_string(&args[idx])?.to_ascii_uppercase();
1245 match s.as_str() {
1246 "NX" => {
1247 flags.nx = true;
1248 idx += 1;
1249 }
1250 "XX" => {
1251 flags.xx = true;
1252 idx += 1;
1253 }
1254 "GT" => {
1255 flags.gt = true;
1256 idx += 1;
1257 }
1258 "LT" => {
1259 flags.lt = true;
1260 idx += 1;
1261 }
1262 "CH" => {
1263 flags.ch = true;
1264 idx += 1;
1265 }
1266 _ => break,
1267 }
1268 }
1269
1270 if flags.nx && flags.xx {
1272 return Err(ProtocolError::InvalidCommandFrame(
1273 "XX and NX options at the same time are not compatible".into(),
1274 ));
1275 }
1276 if flags.gt && flags.lt {
1278 return Err(ProtocolError::InvalidCommandFrame(
1279 "GT and LT options at the same time are not compatible".into(),
1280 ));
1281 }
1282
1283 let remaining = &args[idx..];
1285 if remaining.is_empty() || !remaining.len().is_multiple_of(2) {
1286 return Err(ProtocolError::WrongArity("ZADD".into()));
1287 }
1288
1289 let mut members = Vec::with_capacity(remaining.len() / 2);
1290 for pair in remaining.chunks(2) {
1291 let score = parse_f64(&pair[0], "ZADD")?;
1292 let member = extract_string(&pair[1])?;
1293 members.push((score, member));
1294 }
1295
1296 Ok(Command::ZAdd {
1297 key,
1298 flags,
1299 members,
1300 })
1301}
1302
1303fn parse_zcard(args: &[Frame]) -> Result<Command, ProtocolError> {
1304 if args.len() != 1 {
1305 return Err(ProtocolError::WrongArity("ZCARD".into()));
1306 }
1307 let key = extract_string(&args[0])?;
1308 Ok(Command::ZCard { key })
1309}
1310
1311fn parse_zrem(args: &[Frame]) -> Result<Command, ProtocolError> {
1312 if args.len() < 2 {
1313 return Err(ProtocolError::WrongArity("ZREM".into()));
1314 }
1315 let key = extract_string(&args[0])?;
1316 let members = extract_strings(&args[1..])?;
1317 Ok(Command::ZRem { key, members })
1318}
1319
1320fn parse_zscore(args: &[Frame]) -> Result<Command, ProtocolError> {
1321 if args.len() != 2 {
1322 return Err(ProtocolError::WrongArity("ZSCORE".into()));
1323 }
1324 let key = extract_string(&args[0])?;
1325 let member = extract_string(&args[1])?;
1326 Ok(Command::ZScore { key, member })
1327}
1328
1329fn parse_zrank(args: &[Frame]) -> Result<Command, ProtocolError> {
1330 if args.len() != 2 {
1331 return Err(ProtocolError::WrongArity("ZRANK".into()));
1332 }
1333 let key = extract_string(&args[0])?;
1334 let member = extract_string(&args[1])?;
1335 Ok(Command::ZRank { key, member })
1336}
1337
1338fn parse_zrange(args: &[Frame]) -> Result<Command, ProtocolError> {
1339 if args.len() < 3 || args.len() > 4 {
1340 return Err(ProtocolError::WrongArity("ZRANGE".into()));
1341 }
1342 let key = extract_string(&args[0])?;
1343 let start = parse_i64(&args[1], "ZRANGE")?;
1344 let stop = parse_i64(&args[2], "ZRANGE")?;
1345
1346 let with_scores = if args.len() == 4 {
1347 let opt = extract_string(&args[3])?.to_ascii_uppercase();
1348 if opt != "WITHSCORES" {
1349 return Err(ProtocolError::InvalidCommandFrame(format!(
1350 "unsupported ZRANGE option '{opt}'"
1351 )));
1352 }
1353 true
1354 } else {
1355 false
1356 };
1357
1358 Ok(Command::ZRange {
1359 key,
1360 start,
1361 stop,
1362 with_scores,
1363 })
1364}
1365
1366fn parse_hset(args: &[Frame]) -> Result<Command, ProtocolError> {
1369 if args.len() < 3 || !(args.len() - 1).is_multiple_of(2) {
1373 return Err(ProtocolError::WrongArity("HSET".into()));
1374 }
1375
1376 let key = extract_string(&args[0])?;
1377 let mut fields = Vec::with_capacity((args.len() - 1) / 2);
1378
1379 for chunk in args[1..].chunks(2) {
1380 let field = extract_string(&chunk[0])?;
1381 let value = extract_bytes(&chunk[1])?;
1382 fields.push((field, value));
1383 }
1384
1385 Ok(Command::HSet { key, fields })
1386}
1387
1388fn parse_hget(args: &[Frame]) -> Result<Command, ProtocolError> {
1389 if args.len() != 2 {
1390 return Err(ProtocolError::WrongArity("HGET".into()));
1391 }
1392 let key = extract_string(&args[0])?;
1393 let field = extract_string(&args[1])?;
1394 Ok(Command::HGet { key, field })
1395}
1396
1397fn parse_hgetall(args: &[Frame]) -> Result<Command, ProtocolError> {
1398 if args.len() != 1 {
1399 return Err(ProtocolError::WrongArity("HGETALL".into()));
1400 }
1401 let key = extract_string(&args[0])?;
1402 Ok(Command::HGetAll { key })
1403}
1404
1405fn parse_hdel(args: &[Frame]) -> Result<Command, ProtocolError> {
1406 if args.len() < 2 {
1407 return Err(ProtocolError::WrongArity("HDEL".into()));
1408 }
1409 let key = extract_string(&args[0])?;
1410 let fields = extract_strings(&args[1..])?;
1411 Ok(Command::HDel { key, fields })
1412}
1413
1414fn parse_hexists(args: &[Frame]) -> Result<Command, ProtocolError> {
1415 if args.len() != 2 {
1416 return Err(ProtocolError::WrongArity("HEXISTS".into()));
1417 }
1418 let key = extract_string(&args[0])?;
1419 let field = extract_string(&args[1])?;
1420 Ok(Command::HExists { key, field })
1421}
1422
1423fn parse_hlen(args: &[Frame]) -> Result<Command, ProtocolError> {
1424 if args.len() != 1 {
1425 return Err(ProtocolError::WrongArity("HLEN".into()));
1426 }
1427 let key = extract_string(&args[0])?;
1428 Ok(Command::HLen { key })
1429}
1430
1431fn parse_hincrby(args: &[Frame]) -> Result<Command, ProtocolError> {
1432 if args.len() != 3 {
1433 return Err(ProtocolError::WrongArity("HINCRBY".into()));
1434 }
1435 let key = extract_string(&args[0])?;
1436 let field = extract_string(&args[1])?;
1437 let delta = parse_i64(&args[2], "HINCRBY")?;
1438 Ok(Command::HIncrBy { key, field, delta })
1439}
1440
1441fn parse_hkeys(args: &[Frame]) -> Result<Command, ProtocolError> {
1442 if args.len() != 1 {
1443 return Err(ProtocolError::WrongArity("HKEYS".into()));
1444 }
1445 let key = extract_string(&args[0])?;
1446 Ok(Command::HKeys { key })
1447}
1448
1449fn parse_hvals(args: &[Frame]) -> Result<Command, ProtocolError> {
1450 if args.len() != 1 {
1451 return Err(ProtocolError::WrongArity("HVALS".into()));
1452 }
1453 let key = extract_string(&args[0])?;
1454 Ok(Command::HVals { key })
1455}
1456
1457fn parse_hmget(args: &[Frame]) -> Result<Command, ProtocolError> {
1458 if args.len() < 2 {
1459 return Err(ProtocolError::WrongArity("HMGET".into()));
1460 }
1461 let key = extract_string(&args[0])?;
1462 let fields = extract_strings(&args[1..])?;
1463 Ok(Command::HMGet { key, fields })
1464}
1465
1466fn parse_sadd(args: &[Frame]) -> Result<Command, ProtocolError> {
1469 if args.len() < 2 {
1470 return Err(ProtocolError::WrongArity("SADD".into()));
1471 }
1472 let key = extract_string(&args[0])?;
1473 let members = extract_strings(&args[1..])?;
1474 Ok(Command::SAdd { key, members })
1475}
1476
1477fn parse_srem(args: &[Frame]) -> Result<Command, ProtocolError> {
1478 if args.len() < 2 {
1479 return Err(ProtocolError::WrongArity("SREM".into()));
1480 }
1481 let key = extract_string(&args[0])?;
1482 let members = extract_strings(&args[1..])?;
1483 Ok(Command::SRem { key, members })
1484}
1485
1486fn parse_smembers(args: &[Frame]) -> Result<Command, ProtocolError> {
1487 if args.len() != 1 {
1488 return Err(ProtocolError::WrongArity("SMEMBERS".into()));
1489 }
1490 let key = extract_string(&args[0])?;
1491 Ok(Command::SMembers { key })
1492}
1493
1494fn parse_sismember(args: &[Frame]) -> Result<Command, ProtocolError> {
1495 if args.len() != 2 {
1496 return Err(ProtocolError::WrongArity("SISMEMBER".into()));
1497 }
1498 let key = extract_string(&args[0])?;
1499 let member = extract_string(&args[1])?;
1500 Ok(Command::SIsMember { key, member })
1501}
1502
1503fn parse_scard(args: &[Frame]) -> Result<Command, ProtocolError> {
1504 if args.len() != 1 {
1505 return Err(ProtocolError::WrongArity("SCARD".into()));
1506 }
1507 let key = extract_string(&args[0])?;
1508 Ok(Command::SCard { key })
1509}
1510
1511fn parse_cluster(args: &[Frame]) -> Result<Command, ProtocolError> {
1514 if args.is_empty() {
1515 return Err(ProtocolError::WrongArity("CLUSTER".into()));
1516 }
1517
1518 let subcommand = extract_string(&args[0])?.to_ascii_uppercase();
1519 match subcommand.as_str() {
1520 "INFO" => {
1521 if args.len() != 1 {
1522 return Err(ProtocolError::WrongArity("CLUSTER INFO".into()));
1523 }
1524 Ok(Command::ClusterInfo)
1525 }
1526 "NODES" => {
1527 if args.len() != 1 {
1528 return Err(ProtocolError::WrongArity("CLUSTER NODES".into()));
1529 }
1530 Ok(Command::ClusterNodes)
1531 }
1532 "SLOTS" => {
1533 if args.len() != 1 {
1534 return Err(ProtocolError::WrongArity("CLUSTER SLOTS".into()));
1535 }
1536 Ok(Command::ClusterSlots)
1537 }
1538 "KEYSLOT" => {
1539 if args.len() != 2 {
1540 return Err(ProtocolError::WrongArity("CLUSTER KEYSLOT".into()));
1541 }
1542 let key = extract_string(&args[1])?;
1543 Ok(Command::ClusterKeySlot { key })
1544 }
1545 "MYID" => {
1546 if args.len() != 1 {
1547 return Err(ProtocolError::WrongArity("CLUSTER MYID".into()));
1548 }
1549 Ok(Command::ClusterMyId)
1550 }
1551 "SETSLOT" => parse_cluster_setslot(&args[1..]),
1552 "MEET" => {
1553 if args.len() != 3 {
1554 return Err(ProtocolError::WrongArity("CLUSTER MEET".into()));
1555 }
1556 let ip = extract_string(&args[1])?;
1557 let port_str = extract_string(&args[2])?;
1558 let port: u16 = port_str
1559 .parse()
1560 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid port number".into()))?;
1561 Ok(Command::ClusterMeet { ip, port })
1562 }
1563 "ADDSLOTS" => {
1564 if args.len() < 2 {
1565 return Err(ProtocolError::WrongArity("CLUSTER ADDSLOTS".into()));
1566 }
1567 let slots = parse_slot_list(&args[1..])?;
1568 Ok(Command::ClusterAddSlots { slots })
1569 }
1570 "DELSLOTS" => {
1571 if args.len() < 2 {
1572 return Err(ProtocolError::WrongArity("CLUSTER DELSLOTS".into()));
1573 }
1574 let slots = parse_slot_list(&args[1..])?;
1575 Ok(Command::ClusterDelSlots { slots })
1576 }
1577 "FORGET" => {
1578 if args.len() != 2 {
1579 return Err(ProtocolError::WrongArity("CLUSTER FORGET".into()));
1580 }
1581 let node_id = extract_string(&args[1])?;
1582 Ok(Command::ClusterForget { node_id })
1583 }
1584 "REPLICATE" => {
1585 if args.len() != 2 {
1586 return Err(ProtocolError::WrongArity("CLUSTER REPLICATE".into()));
1587 }
1588 let node_id = extract_string(&args[1])?;
1589 Ok(Command::ClusterReplicate { node_id })
1590 }
1591 "FAILOVER" => {
1592 let mut force = false;
1593 let mut takeover = false;
1594 for arg in &args[1..] {
1595 let opt = extract_string(arg)?.to_ascii_uppercase();
1596 match opt.as_str() {
1597 "FORCE" => force = true,
1598 "TAKEOVER" => takeover = true,
1599 _ => {
1600 return Err(ProtocolError::InvalidCommandFrame(format!(
1601 "unknown CLUSTER FAILOVER option '{opt}'"
1602 )))
1603 }
1604 }
1605 }
1606 Ok(Command::ClusterFailover { force, takeover })
1607 }
1608 "COUNTKEYSINSLOT" => {
1609 if args.len() != 2 {
1610 return Err(ProtocolError::WrongArity("CLUSTER COUNTKEYSINSLOT".into()));
1611 }
1612 let slot_str = extract_string(&args[1])?;
1613 let slot: u16 = slot_str
1614 .parse()
1615 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1616 Ok(Command::ClusterCountKeysInSlot { slot })
1617 }
1618 "GETKEYSINSLOT" => {
1619 if args.len() != 3 {
1620 return Err(ProtocolError::WrongArity("CLUSTER GETKEYSINSLOT".into()));
1621 }
1622 let slot_str = extract_string(&args[1])?;
1623 let slot: u16 = slot_str
1624 .parse()
1625 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1626 let count_str = extract_string(&args[2])?;
1627 let count: u32 = count_str
1628 .parse()
1629 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid count".into()))?;
1630 Ok(Command::ClusterGetKeysInSlot { slot, count })
1631 }
1632 _ => Err(ProtocolError::InvalidCommandFrame(format!(
1633 "unknown CLUSTER subcommand '{subcommand}'"
1634 ))),
1635 }
1636}
1637
1638fn parse_asking(args: &[Frame]) -> Result<Command, ProtocolError> {
1639 if !args.is_empty() {
1640 return Err(ProtocolError::WrongArity("ASKING".into()));
1641 }
1642 Ok(Command::Asking)
1643}
1644
1645fn parse_slowlog(args: &[Frame]) -> Result<Command, ProtocolError> {
1646 if args.is_empty() {
1647 return Err(ProtocolError::WrongArity("SLOWLOG".into()));
1648 }
1649
1650 let subcmd = extract_string(&args[0])?.to_ascii_uppercase();
1651 match subcmd.as_str() {
1652 "GET" => {
1653 let count = if args.len() > 1 {
1654 let n: usize = extract_string(&args[1])?.parse().map_err(|_| {
1655 ProtocolError::InvalidCommandFrame("invalid count for SLOWLOG GET".into())
1656 })?;
1657 Some(n)
1658 } else {
1659 None
1660 };
1661 Ok(Command::SlowLogGet { count })
1662 }
1663 "LEN" => Ok(Command::SlowLogLen),
1664 "RESET" => Ok(Command::SlowLogReset),
1665 other => Err(ProtocolError::InvalidCommandFrame(format!(
1666 "unknown SLOWLOG subcommand '{other}'"
1667 ))),
1668 }
1669}
1670
1671fn parse_slot_list(args: &[Frame]) -> Result<Vec<u16>, ProtocolError> {
1672 let mut slots = Vec::with_capacity(args.len());
1673 for arg in args {
1674 let slot_str = extract_string(arg)?;
1675 let slot: u16 = slot_str
1676 .parse()
1677 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1678 if slot >= 16384 {
1679 return Err(ProtocolError::InvalidCommandFrame(format!(
1680 "invalid slot {slot}: must be 0-16383"
1681 )));
1682 }
1683 slots.push(slot);
1684 }
1685 Ok(slots)
1686}
1687
1688fn parse_cluster_setslot(args: &[Frame]) -> Result<Command, ProtocolError> {
1689 if args.is_empty() {
1690 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT".into()));
1691 }
1692
1693 let slot_str = extract_string(&args[0])?;
1694 let slot: u16 = slot_str
1695 .parse()
1696 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid slot number".into()))?;
1697 if slot >= 16384 {
1698 return Err(ProtocolError::InvalidCommandFrame(format!(
1699 "invalid slot {slot}: must be 0-16383"
1700 )));
1701 }
1702
1703 if args.len() < 2 {
1704 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT".into()));
1705 }
1706
1707 let action = extract_string(&args[1])?.to_ascii_uppercase();
1708 match action.as_str() {
1709 "IMPORTING" => {
1710 if args.len() != 3 {
1711 return Err(ProtocolError::WrongArity(
1712 "CLUSTER SETSLOT IMPORTING".into(),
1713 ));
1714 }
1715 let node_id = extract_string(&args[2])?;
1716 Ok(Command::ClusterSetSlotImporting { slot, node_id })
1717 }
1718 "MIGRATING" => {
1719 if args.len() != 3 {
1720 return Err(ProtocolError::WrongArity(
1721 "CLUSTER SETSLOT MIGRATING".into(),
1722 ));
1723 }
1724 let node_id = extract_string(&args[2])?;
1725 Ok(Command::ClusterSetSlotMigrating { slot, node_id })
1726 }
1727 "NODE" => {
1728 if args.len() != 3 {
1729 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT NODE".into()));
1730 }
1731 let node_id = extract_string(&args[2])?;
1732 Ok(Command::ClusterSetSlotNode { slot, node_id })
1733 }
1734 "STABLE" => {
1735 if args.len() != 2 {
1736 return Err(ProtocolError::WrongArity("CLUSTER SETSLOT STABLE".into()));
1737 }
1738 Ok(Command::ClusterSetSlotStable { slot })
1739 }
1740 _ => Err(ProtocolError::InvalidCommandFrame(format!(
1741 "unknown CLUSTER SETSLOT action '{action}'"
1742 ))),
1743 }
1744}
1745
1746fn parse_migrate(args: &[Frame]) -> Result<Command, ProtocolError> {
1747 if args.len() < 5 {
1749 return Err(ProtocolError::WrongArity("MIGRATE".into()));
1750 }
1751
1752 let host = extract_string(&args[0])?;
1753 let port_str = extract_string(&args[1])?;
1754 let port: u16 = port_str
1755 .parse()
1756 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid port number".into()))?;
1757 let key = extract_string(&args[2])?;
1758 let db_str = extract_string(&args[3])?;
1759 let db: u32 = db_str
1760 .parse()
1761 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid db number".into()))?;
1762 let timeout_str = extract_string(&args[4])?;
1763 let timeout_ms: u64 = timeout_str
1764 .parse()
1765 .map_err(|_| ProtocolError::InvalidCommandFrame("invalid timeout".into()))?;
1766
1767 let mut copy = false;
1768 let mut replace = false;
1769
1770 for arg in &args[5..] {
1771 let opt = extract_string(arg)?.to_ascii_uppercase();
1772 match opt.as_str() {
1773 "COPY" => copy = true,
1774 "REPLACE" => replace = true,
1775 _ => {
1776 return Err(ProtocolError::InvalidCommandFrame(format!(
1777 "unknown MIGRATE option '{opt}'"
1778 )))
1779 }
1780 }
1781 }
1782
1783 Ok(Command::Migrate {
1784 host,
1785 port,
1786 key,
1787 db,
1788 timeout_ms,
1789 copy,
1790 replace,
1791 })
1792}
1793
1794fn parse_subscribe(args: &[Frame]) -> Result<Command, ProtocolError> {
1795 if args.is_empty() {
1796 return Err(ProtocolError::WrongArity("SUBSCRIBE".into()));
1797 }
1798 let channels: Vec<String> = args.iter().map(extract_string).collect::<Result<_, _>>()?;
1799 Ok(Command::Subscribe { channels })
1800}
1801
1802fn parse_unsubscribe(args: &[Frame]) -> Result<Command, ProtocolError> {
1803 let channels: Vec<String> = args.iter().map(extract_string).collect::<Result<_, _>>()?;
1804 Ok(Command::Unsubscribe { channels })
1805}
1806
1807fn parse_psubscribe(args: &[Frame]) -> Result<Command, ProtocolError> {
1808 if args.is_empty() {
1809 return Err(ProtocolError::WrongArity("PSUBSCRIBE".into()));
1810 }
1811 let patterns: Vec<String> = args.iter().map(extract_string).collect::<Result<_, _>>()?;
1812 Ok(Command::PSubscribe { patterns })
1813}
1814
1815fn parse_punsubscribe(args: &[Frame]) -> Result<Command, ProtocolError> {
1816 let patterns: Vec<String> = args.iter().map(extract_string).collect::<Result<_, _>>()?;
1817 Ok(Command::PUnsubscribe { patterns })
1818}
1819
1820fn parse_publish(args: &[Frame]) -> Result<Command, ProtocolError> {
1821 if args.len() != 2 {
1822 return Err(ProtocolError::WrongArity("PUBLISH".into()));
1823 }
1824 let channel = extract_string(&args[0])?;
1825 let message = extract_bytes(&args[1])?;
1826 Ok(Command::Publish { channel, message })
1827}
1828
1829fn parse_pubsub(args: &[Frame]) -> Result<Command, ProtocolError> {
1830 if args.is_empty() {
1831 return Err(ProtocolError::WrongArity("PUBSUB".into()));
1832 }
1833
1834 let subcmd = extract_string(&args[0])?.to_ascii_uppercase();
1835 match subcmd.as_str() {
1836 "CHANNELS" => {
1837 let pattern = if args.len() > 1 {
1838 Some(extract_string(&args[1])?)
1839 } else {
1840 None
1841 };
1842 Ok(Command::PubSubChannels { pattern })
1843 }
1844 "NUMSUB" => {
1845 let channels: Vec<String> = args[1..]
1846 .iter()
1847 .map(extract_string)
1848 .collect::<Result<_, _>>()?;
1849 Ok(Command::PubSubNumSub { channels })
1850 }
1851 "NUMPAT" => Ok(Command::PubSubNumPat),
1852 other => Err(ProtocolError::InvalidCommandFrame(format!(
1853 "unknown PUBSUB subcommand '{other}'"
1854 ))),
1855 }
1856}
1857
1858fn parse_vadd(args: &[Frame]) -> Result<Command, ProtocolError> {
1862 if args.len() < 3 {
1864 return Err(ProtocolError::WrongArity("VADD".into()));
1865 }
1866
1867 let key = extract_string(&args[0])?;
1868 let element = extract_string(&args[1])?;
1869
1870 let mut idx = 2;
1872 let mut vector = Vec::new();
1873 while idx < args.len() {
1874 if vector.len() >= MAX_VECTOR_DIMS {
1875 return Err(ProtocolError::InvalidCommandFrame(format!(
1876 "VADD: vector exceeds {MAX_VECTOR_DIMS} dimensions"
1877 )));
1878 }
1879 let s = extract_string(&args[idx])?;
1880 if let Ok(v) = s.parse::<f32>() {
1881 if v.is_nan() || v.is_infinite() {
1882 return Err(ProtocolError::InvalidCommandFrame(
1883 "VADD: vector components must be finite (no NaN/infinity)".into(),
1884 ));
1885 }
1886 vector.push(v);
1887 idx += 1;
1888 } else {
1889 break;
1890 }
1891 }
1892
1893 if vector.is_empty() {
1894 return Err(ProtocolError::InvalidCommandFrame(
1895 "VADD: at least one vector dimension required".into(),
1896 ));
1897 }
1898
1899 let mut metric: u8 = 0; let mut quantization: u8 = 0; let mut connectivity: u32 = 16;
1903 let mut expansion_add: u32 = 64;
1904
1905 while idx < args.len() {
1906 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
1907 match flag.as_str() {
1908 "METRIC" => {
1909 idx += 1;
1910 if idx >= args.len() {
1911 return Err(ProtocolError::InvalidCommandFrame(
1912 "VADD: METRIC requires a value".into(),
1913 ));
1914 }
1915 let val = extract_string(&args[idx])?.to_ascii_uppercase();
1916 metric = match val.as_str() {
1917 "COSINE" => 0,
1918 "L2" => 1,
1919 "IP" => 2,
1920 _ => {
1921 return Err(ProtocolError::InvalidCommandFrame(format!(
1922 "VADD: unknown metric '{val}'"
1923 )))
1924 }
1925 };
1926 idx += 1;
1927 }
1928 "QUANT" => {
1929 idx += 1;
1930 if idx >= args.len() {
1931 return Err(ProtocolError::InvalidCommandFrame(
1932 "VADD: QUANT requires a value".into(),
1933 ));
1934 }
1935 let val = extract_string(&args[idx])?.to_ascii_uppercase();
1936 quantization = match val.as_str() {
1937 "F32" => 0,
1938 "F16" => 1,
1939 "I8" | "Q8" => 2,
1940 _ => {
1941 return Err(ProtocolError::InvalidCommandFrame(format!(
1942 "VADD: unknown quantization '{val}'"
1943 )))
1944 }
1945 };
1946 idx += 1;
1947 }
1948 "M" => {
1949 idx += 1;
1950 if idx >= args.len() {
1951 return Err(ProtocolError::InvalidCommandFrame(
1952 "VADD: M requires a value".into(),
1953 ));
1954 }
1955 let m = parse_u64(&args[idx], "VADD")?;
1956 if m > MAX_HNSW_PARAM {
1957 return Err(ProtocolError::InvalidCommandFrame(format!(
1958 "VADD: M value {m} exceeds max {MAX_HNSW_PARAM}"
1959 )));
1960 }
1961 connectivity = m as u32;
1962 idx += 1;
1963 }
1964 "EF" => {
1965 idx += 1;
1966 if idx >= args.len() {
1967 return Err(ProtocolError::InvalidCommandFrame(
1968 "VADD: EF requires a value".into(),
1969 ));
1970 }
1971 let ef = parse_u64(&args[idx], "VADD")?;
1972 if ef > MAX_HNSW_PARAM {
1973 return Err(ProtocolError::InvalidCommandFrame(format!(
1974 "VADD: EF value {ef} exceeds max {MAX_HNSW_PARAM}"
1975 )));
1976 }
1977 expansion_add = ef as u32;
1978 idx += 1;
1979 }
1980 _ => {
1981 return Err(ProtocolError::InvalidCommandFrame(format!(
1982 "VADD: unexpected argument '{flag}'"
1983 )));
1984 }
1985 }
1986 }
1987
1988 Ok(Command::VAdd {
1989 key,
1990 element,
1991 vector,
1992 metric,
1993 quantization,
1994 connectivity,
1995 expansion_add,
1996 })
1997}
1998
1999fn parse_vadd_batch(args: &[Frame]) -> Result<Command, ProtocolError> {
2002 if args.len() < 3 {
2004 return Err(ProtocolError::WrongArity("VADD_BATCH".into()));
2005 }
2006
2007 let key = extract_string(&args[0])?;
2008
2009 let dim_kw = extract_string(&args[1])?.to_ascii_uppercase();
2011 if dim_kw != "DIM" {
2012 return Err(ProtocolError::InvalidCommandFrame(
2013 "VADD_BATCH: expected DIM keyword".into(),
2014 ));
2015 }
2016
2017 let dim = parse_u64(&args[2], "VADD_BATCH")? as usize;
2018 if dim == 0 {
2019 return Err(ProtocolError::InvalidCommandFrame(
2020 "VADD_BATCH: DIM must be at least 1".into(),
2021 ));
2022 }
2023 if dim > MAX_VECTOR_DIMS {
2024 return Err(ProtocolError::InvalidCommandFrame(format!(
2025 "VADD_BATCH: DIM {dim} exceeds max {MAX_VECTOR_DIMS}"
2026 )));
2027 }
2028
2029 let mut idx = 3;
2034 let mut entries: Vec<(String, Vec<f32>)> = Vec::new();
2035 let entry_len = 1 + dim; while idx < args.len() {
2038 if idx + entry_len > args.len() {
2040 break;
2041 }
2042
2043 if dim > 0 {
2046 let peek = extract_string(&args[idx + 1])?;
2047 if peek.parse::<f32>().is_err() {
2048 break;
2049 }
2050 }
2051
2052 let element = extract_string(&args[idx])?;
2053 idx += 1;
2054
2055 let mut vector = Vec::with_capacity(dim);
2056 for _ in 0..dim {
2057 let s = extract_string(&args[idx])?;
2058 let v = s.parse::<f32>().map_err(|_| {
2059 ProtocolError::InvalidCommandFrame(format!("VADD_BATCH: expected float, got '{s}'"))
2060 })?;
2061 if v.is_nan() || v.is_infinite() {
2062 return Err(ProtocolError::InvalidCommandFrame(
2063 "VADD_BATCH: vector components must be finite (no NaN/infinity)".into(),
2064 ));
2065 }
2066 vector.push(v);
2067 idx += 1;
2068 }
2069
2070 entries.push((element, vector));
2071
2072 if entries.len() >= MAX_VADD_BATCH_SIZE {
2073 return Err(ProtocolError::InvalidCommandFrame(format!(
2074 "VADD_BATCH: batch size exceeds max {MAX_VADD_BATCH_SIZE}"
2075 )));
2076 }
2077 }
2078
2079 let mut metric: u8 = 0;
2081 let mut quantization: u8 = 0;
2082 let mut connectivity: u32 = 16;
2083 let mut expansion_add: u32 = 64;
2084
2085 while idx < args.len() {
2086 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
2087 match flag.as_str() {
2088 "METRIC" => {
2089 idx += 1;
2090 if idx >= args.len() {
2091 return Err(ProtocolError::InvalidCommandFrame(
2092 "VADD_BATCH: METRIC requires a value".into(),
2093 ));
2094 }
2095 let val = extract_string(&args[idx])?.to_ascii_uppercase();
2096 metric = match val.as_str() {
2097 "COSINE" => 0,
2098 "L2" => 1,
2099 "IP" => 2,
2100 _ => {
2101 return Err(ProtocolError::InvalidCommandFrame(format!(
2102 "VADD_BATCH: unknown metric '{val}'"
2103 )))
2104 }
2105 };
2106 idx += 1;
2107 }
2108 "QUANT" => {
2109 idx += 1;
2110 if idx >= args.len() {
2111 return Err(ProtocolError::InvalidCommandFrame(
2112 "VADD_BATCH: QUANT requires a value".into(),
2113 ));
2114 }
2115 let val = extract_string(&args[idx])?.to_ascii_uppercase();
2116 quantization = match val.as_str() {
2117 "F32" => 0,
2118 "F16" => 1,
2119 "I8" | "Q8" => 2,
2120 _ => {
2121 return Err(ProtocolError::InvalidCommandFrame(format!(
2122 "VADD_BATCH: unknown quantization '{val}'"
2123 )))
2124 }
2125 };
2126 idx += 1;
2127 }
2128 "M" => {
2129 idx += 1;
2130 if idx >= args.len() {
2131 return Err(ProtocolError::InvalidCommandFrame(
2132 "VADD_BATCH: M requires a value".into(),
2133 ));
2134 }
2135 let m = parse_u64(&args[idx], "VADD_BATCH")?;
2136 if m > MAX_HNSW_PARAM {
2137 return Err(ProtocolError::InvalidCommandFrame(format!(
2138 "VADD_BATCH: M value {m} exceeds max {MAX_HNSW_PARAM}"
2139 )));
2140 }
2141 connectivity = m as u32;
2142 idx += 1;
2143 }
2144 "EF" => {
2145 idx += 1;
2146 if idx >= args.len() {
2147 return Err(ProtocolError::InvalidCommandFrame(
2148 "VADD_BATCH: EF requires a value".into(),
2149 ));
2150 }
2151 let ef = parse_u64(&args[idx], "VADD_BATCH")?;
2152 if ef > MAX_HNSW_PARAM {
2153 return Err(ProtocolError::InvalidCommandFrame(format!(
2154 "VADD_BATCH: EF value {ef} exceeds max {MAX_HNSW_PARAM}"
2155 )));
2156 }
2157 expansion_add = ef as u32;
2158 idx += 1;
2159 }
2160 _ => {
2161 return Err(ProtocolError::InvalidCommandFrame(format!(
2162 "VADD_BATCH: unexpected argument '{flag}'"
2163 )));
2164 }
2165 }
2166 }
2167
2168 Ok(Command::VAddBatch {
2169 key,
2170 entries,
2171 dim,
2172 metric,
2173 quantization,
2174 connectivity,
2175 expansion_add,
2176 })
2177}
2178
2179fn parse_vsim(args: &[Frame]) -> Result<Command, ProtocolError> {
2181 if args.len() < 4 {
2183 return Err(ProtocolError::WrongArity("VSIM".into()));
2184 }
2185
2186 let key = extract_string(&args[0])?;
2187
2188 let mut idx = 1;
2190 let mut query = Vec::new();
2191 while idx < args.len() {
2192 if query.len() >= MAX_VECTOR_DIMS {
2193 return Err(ProtocolError::InvalidCommandFrame(format!(
2194 "VSIM: query exceeds {MAX_VECTOR_DIMS} dimensions"
2195 )));
2196 }
2197 let s = extract_string(&args[idx])?;
2198 if let Ok(v) = s.parse::<f32>() {
2199 if v.is_nan() || v.is_infinite() {
2200 return Err(ProtocolError::InvalidCommandFrame(
2201 "VSIM: query components must be finite (no NaN/infinity)".into(),
2202 ));
2203 }
2204 query.push(v);
2205 idx += 1;
2206 } else {
2207 break;
2208 }
2209 }
2210
2211 if query.is_empty() {
2212 return Err(ProtocolError::InvalidCommandFrame(
2213 "VSIM: at least one query dimension required".into(),
2214 ));
2215 }
2216
2217 let mut count: Option<usize> = None;
2219 let mut ef_search: usize = 0;
2220 let mut with_scores = false;
2221
2222 while idx < args.len() {
2223 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
2224 match flag.as_str() {
2225 "COUNT" => {
2226 idx += 1;
2227 if idx >= args.len() {
2228 return Err(ProtocolError::InvalidCommandFrame(
2229 "VSIM: COUNT requires a value".into(),
2230 ));
2231 }
2232 let c = parse_u64(&args[idx], "VSIM")?;
2233 if c > MAX_VSIM_COUNT {
2234 return Err(ProtocolError::InvalidCommandFrame(format!(
2235 "VSIM: COUNT {c} exceeds max {MAX_VSIM_COUNT}"
2236 )));
2237 }
2238 count = Some(c as usize);
2239 idx += 1;
2240 }
2241 "EF" => {
2242 idx += 1;
2243 if idx >= args.len() {
2244 return Err(ProtocolError::InvalidCommandFrame(
2245 "VSIM: EF requires a value".into(),
2246 ));
2247 }
2248 let ef = parse_u64(&args[idx], "VSIM")?;
2249 if ef > MAX_VSIM_EF {
2250 return Err(ProtocolError::InvalidCommandFrame(format!(
2251 "VSIM: EF {ef} exceeds max {MAX_VSIM_EF}"
2252 )));
2253 }
2254 ef_search = ef as usize;
2255 idx += 1;
2256 }
2257 "WITHSCORES" => {
2258 with_scores = true;
2259 idx += 1;
2260 }
2261 _ => {
2262 return Err(ProtocolError::InvalidCommandFrame(format!(
2263 "VSIM: unexpected argument '{flag}'"
2264 )));
2265 }
2266 }
2267 }
2268
2269 let count = count
2270 .ok_or_else(|| ProtocolError::InvalidCommandFrame("VSIM: COUNT is required".into()))?;
2271
2272 Ok(Command::VSim {
2273 key,
2274 query,
2275 count,
2276 ef_search,
2277 with_scores,
2278 })
2279}
2280
2281fn parse_vrem(args: &[Frame]) -> Result<Command, ProtocolError> {
2283 if args.len() != 2 {
2284 return Err(ProtocolError::WrongArity("VREM".into()));
2285 }
2286 let key = extract_string(&args[0])?;
2287 let element = extract_string(&args[1])?;
2288 Ok(Command::VRem { key, element })
2289}
2290
2291fn parse_vget(args: &[Frame]) -> Result<Command, ProtocolError> {
2293 if args.len() != 2 {
2294 return Err(ProtocolError::WrongArity("VGET".into()));
2295 }
2296 let key = extract_string(&args[0])?;
2297 let element = extract_string(&args[1])?;
2298 Ok(Command::VGet { key, element })
2299}
2300
2301fn parse_vcard(args: &[Frame]) -> Result<Command, ProtocolError> {
2303 if args.len() != 1 {
2304 return Err(ProtocolError::WrongArity("VCARD".into()));
2305 }
2306 let key = extract_string(&args[0])?;
2307 Ok(Command::VCard { key })
2308}
2309
2310fn parse_vdim(args: &[Frame]) -> Result<Command, ProtocolError> {
2312 if args.len() != 1 {
2313 return Err(ProtocolError::WrongArity("VDIM".into()));
2314 }
2315 let key = extract_string(&args[0])?;
2316 Ok(Command::VDim { key })
2317}
2318
2319fn parse_vinfo(args: &[Frame]) -> Result<Command, ProtocolError> {
2321 if args.len() != 1 {
2322 return Err(ProtocolError::WrongArity("VINFO".into()));
2323 }
2324 let key = extract_string(&args[0])?;
2325 Ok(Command::VInfo { key })
2326}
2327
2328fn parse_proto_register(args: &[Frame]) -> Result<Command, ProtocolError> {
2331 if args.len() != 2 {
2332 return Err(ProtocolError::WrongArity("PROTO.REGISTER".into()));
2333 }
2334 let name = extract_string(&args[0])?;
2335 let descriptor = extract_bytes(&args[1])?;
2336 Ok(Command::ProtoRegister { name, descriptor })
2337}
2338
2339fn parse_proto_set(args: &[Frame]) -> Result<Command, ProtocolError> {
2340 if args.len() < 3 {
2341 return Err(ProtocolError::WrongArity("PROTO.SET".into()));
2342 }
2343
2344 let key = extract_string(&args[0])?;
2345 let type_name = extract_string(&args[1])?;
2346 let data = extract_bytes(&args[2])?;
2347
2348 let mut expire = None;
2349 let mut nx = false;
2350 let mut xx = false;
2351 let mut idx = 3;
2352
2353 while idx < args.len() {
2354 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
2355 match flag.as_str() {
2356 "NX" => {
2357 nx = true;
2358 idx += 1;
2359 }
2360 "XX" => {
2361 xx = true;
2362 idx += 1;
2363 }
2364 "EX" => {
2365 idx += 1;
2366 if idx >= args.len() {
2367 return Err(ProtocolError::WrongArity("PROTO.SET".into()));
2368 }
2369 let amount = parse_u64(&args[idx], "PROTO.SET")?;
2370 if amount == 0 {
2371 return Err(ProtocolError::InvalidCommandFrame(
2372 "invalid expire time in 'PROTO.SET' command".into(),
2373 ));
2374 }
2375 expire = Some(SetExpire::Ex(amount));
2376 idx += 1;
2377 }
2378 "PX" => {
2379 idx += 1;
2380 if idx >= args.len() {
2381 return Err(ProtocolError::WrongArity("PROTO.SET".into()));
2382 }
2383 let amount = parse_u64(&args[idx], "PROTO.SET")?;
2384 if amount == 0 {
2385 return Err(ProtocolError::InvalidCommandFrame(
2386 "invalid expire time in 'PROTO.SET' command".into(),
2387 ));
2388 }
2389 expire = Some(SetExpire::Px(amount));
2390 idx += 1;
2391 }
2392 _ => {
2393 return Err(ProtocolError::InvalidCommandFrame(format!(
2394 "unsupported PROTO.SET option '{flag}'"
2395 )));
2396 }
2397 }
2398 }
2399
2400 if nx && xx {
2401 return Err(ProtocolError::InvalidCommandFrame(
2402 "XX and NX options at the same time are not compatible".into(),
2403 ));
2404 }
2405
2406 Ok(Command::ProtoSet {
2407 key,
2408 type_name,
2409 data,
2410 expire,
2411 nx,
2412 xx,
2413 })
2414}
2415
2416fn parse_proto_get(args: &[Frame]) -> Result<Command, ProtocolError> {
2417 if args.len() != 1 {
2418 return Err(ProtocolError::WrongArity("PROTO.GET".into()));
2419 }
2420 let key = extract_string(&args[0])?;
2421 Ok(Command::ProtoGet { key })
2422}
2423
2424fn parse_proto_type(args: &[Frame]) -> Result<Command, ProtocolError> {
2425 if args.len() != 1 {
2426 return Err(ProtocolError::WrongArity("PROTO.TYPE".into()));
2427 }
2428 let key = extract_string(&args[0])?;
2429 Ok(Command::ProtoType { key })
2430}
2431
2432fn parse_proto_schemas(args: &[Frame]) -> Result<Command, ProtocolError> {
2433 if !args.is_empty() {
2434 return Err(ProtocolError::WrongArity("PROTO.SCHEMAS".into()));
2435 }
2436 Ok(Command::ProtoSchemas)
2437}
2438
2439fn parse_proto_describe(args: &[Frame]) -> Result<Command, ProtocolError> {
2440 if args.len() != 1 {
2441 return Err(ProtocolError::WrongArity("PROTO.DESCRIBE".into()));
2442 }
2443 let name = extract_string(&args[0])?;
2444 Ok(Command::ProtoDescribe { name })
2445}
2446
2447fn parse_proto_getfield(args: &[Frame]) -> Result<Command, ProtocolError> {
2448 if args.len() != 2 {
2449 return Err(ProtocolError::WrongArity("PROTO.GETFIELD".into()));
2450 }
2451 let key = extract_string(&args[0])?;
2452 let field_path = extract_string(&args[1])?;
2453 Ok(Command::ProtoGetField { key, field_path })
2454}
2455
2456fn parse_proto_setfield(args: &[Frame]) -> Result<Command, ProtocolError> {
2457 if args.len() != 3 {
2458 return Err(ProtocolError::WrongArity("PROTO.SETFIELD".into()));
2459 }
2460 let key = extract_string(&args[0])?;
2461 let field_path = extract_string(&args[1])?;
2462 let value = extract_string(&args[2])?;
2463 Ok(Command::ProtoSetField {
2464 key,
2465 field_path,
2466 value,
2467 })
2468}
2469
2470fn parse_proto_delfield(args: &[Frame]) -> Result<Command, ProtocolError> {
2471 if args.len() != 2 {
2472 return Err(ProtocolError::WrongArity("PROTO.DELFIELD".into()));
2473 }
2474 let key = extract_string(&args[0])?;
2475 let field_path = extract_string(&args[1])?;
2476 Ok(Command::ProtoDelField { key, field_path })
2477}
2478
2479fn parse_auth(args: &[Frame]) -> Result<Command, ProtocolError> {
2480 match args.len() {
2481 1 => {
2482 let password = extract_string(&args[0])?;
2483 Ok(Command::Auth {
2484 username: None,
2485 password,
2486 })
2487 }
2488 2 => {
2489 let username = extract_string(&args[0])?;
2490 let password = extract_string(&args[1])?;
2491 Ok(Command::Auth {
2492 username: Some(username),
2493 password,
2494 })
2495 }
2496 _ => Err(ProtocolError::WrongArity("AUTH".into())),
2497 }
2498}
2499
2500fn parse_quit(args: &[Frame]) -> Result<Command, ProtocolError> {
2501 if !args.is_empty() {
2502 return Err(ProtocolError::WrongArity("QUIT".into()));
2503 }
2504 Ok(Command::Quit)
2505}
2506
2507#[cfg(test)]
2508mod tests {
2509 use super::*;
2510
2511 fn cmd(parts: &[&str]) -> Frame {
2513 Frame::Array(
2514 parts
2515 .iter()
2516 .map(|s| Frame::Bulk(Bytes::from(s.to_string())))
2517 .collect(),
2518 )
2519 }
2520
2521 #[test]
2524 fn ping_no_args() {
2525 assert_eq!(
2526 Command::from_frame(cmd(&["PING"])).unwrap(),
2527 Command::Ping(None),
2528 );
2529 }
2530
2531 #[test]
2532 fn ping_with_message() {
2533 assert_eq!(
2534 Command::from_frame(cmd(&["PING", "hello"])).unwrap(),
2535 Command::Ping(Some(Bytes::from("hello"))),
2536 );
2537 }
2538
2539 #[test]
2540 fn ping_case_insensitive() {
2541 assert_eq!(
2542 Command::from_frame(cmd(&["ping"])).unwrap(),
2543 Command::Ping(None),
2544 );
2545 assert_eq!(
2546 Command::from_frame(cmd(&["Ping"])).unwrap(),
2547 Command::Ping(None),
2548 );
2549 }
2550
2551 #[test]
2552 fn ping_too_many_args() {
2553 let err = Command::from_frame(cmd(&["PING", "a", "b"])).unwrap_err();
2554 assert!(matches!(err, ProtocolError::WrongArity(_)));
2555 }
2556
2557 #[test]
2560 fn echo() {
2561 assert_eq!(
2562 Command::from_frame(cmd(&["ECHO", "test"])).unwrap(),
2563 Command::Echo(Bytes::from("test")),
2564 );
2565 }
2566
2567 #[test]
2568 fn echo_missing_arg() {
2569 let err = Command::from_frame(cmd(&["ECHO"])).unwrap_err();
2570 assert!(matches!(err, ProtocolError::WrongArity(_)));
2571 }
2572
2573 #[test]
2576 fn get_basic() {
2577 assert_eq!(
2578 Command::from_frame(cmd(&["GET", "mykey"])).unwrap(),
2579 Command::Get {
2580 key: "mykey".into()
2581 },
2582 );
2583 }
2584
2585 #[test]
2586 fn get_no_args() {
2587 let err = Command::from_frame(cmd(&["GET"])).unwrap_err();
2588 assert!(matches!(err, ProtocolError::WrongArity(_)));
2589 }
2590
2591 #[test]
2592 fn get_too_many_args() {
2593 let err = Command::from_frame(cmd(&["GET", "a", "b"])).unwrap_err();
2594 assert!(matches!(err, ProtocolError::WrongArity(_)));
2595 }
2596
2597 #[test]
2598 fn get_case_insensitive() {
2599 assert_eq!(
2600 Command::from_frame(cmd(&["get", "k"])).unwrap(),
2601 Command::Get { key: "k".into() },
2602 );
2603 }
2604
2605 #[test]
2608 fn set_basic() {
2609 assert_eq!(
2610 Command::from_frame(cmd(&["SET", "key", "value"])).unwrap(),
2611 Command::Set {
2612 key: "key".into(),
2613 value: Bytes::from("value"),
2614 expire: None,
2615 nx: false,
2616 xx: false,
2617 },
2618 );
2619 }
2620
2621 #[test]
2622 fn set_with_ex() {
2623 assert_eq!(
2624 Command::from_frame(cmd(&["SET", "key", "val", "EX", "10"])).unwrap(),
2625 Command::Set {
2626 key: "key".into(),
2627 value: Bytes::from("val"),
2628 expire: Some(SetExpire::Ex(10)),
2629 nx: false,
2630 xx: false,
2631 },
2632 );
2633 }
2634
2635 #[test]
2636 fn set_with_px() {
2637 assert_eq!(
2638 Command::from_frame(cmd(&["SET", "key", "val", "PX", "5000"])).unwrap(),
2639 Command::Set {
2640 key: "key".into(),
2641 value: Bytes::from("val"),
2642 expire: Some(SetExpire::Px(5000)),
2643 nx: false,
2644 xx: false,
2645 },
2646 );
2647 }
2648
2649 #[test]
2650 fn set_ex_case_insensitive() {
2651 assert_eq!(
2652 Command::from_frame(cmd(&["set", "k", "v", "ex", "5"])).unwrap(),
2653 Command::Set {
2654 key: "k".into(),
2655 value: Bytes::from("v"),
2656 expire: Some(SetExpire::Ex(5)),
2657 nx: false,
2658 xx: false,
2659 },
2660 );
2661 }
2662
2663 #[test]
2664 fn set_nx_flag() {
2665 assert_eq!(
2666 Command::from_frame(cmd(&["SET", "key", "val", "NX"])).unwrap(),
2667 Command::Set {
2668 key: "key".into(),
2669 value: Bytes::from("val"),
2670 expire: None,
2671 nx: true,
2672 xx: false,
2673 },
2674 );
2675 }
2676
2677 #[test]
2678 fn set_xx_flag() {
2679 assert_eq!(
2680 Command::from_frame(cmd(&["SET", "key", "val", "XX"])).unwrap(),
2681 Command::Set {
2682 key: "key".into(),
2683 value: Bytes::from("val"),
2684 expire: None,
2685 nx: false,
2686 xx: true,
2687 },
2688 );
2689 }
2690
2691 #[test]
2692 fn set_nx_with_ex() {
2693 assert_eq!(
2694 Command::from_frame(cmd(&["SET", "key", "val", "EX", "10", "NX"])).unwrap(),
2695 Command::Set {
2696 key: "key".into(),
2697 value: Bytes::from("val"),
2698 expire: Some(SetExpire::Ex(10)),
2699 nx: true,
2700 xx: false,
2701 },
2702 );
2703 }
2704
2705 #[test]
2706 fn set_nx_before_ex() {
2707 assert_eq!(
2708 Command::from_frame(cmd(&["SET", "key", "val", "NX", "PX", "5000"])).unwrap(),
2709 Command::Set {
2710 key: "key".into(),
2711 value: Bytes::from("val"),
2712 expire: Some(SetExpire::Px(5000)),
2713 nx: true,
2714 xx: false,
2715 },
2716 );
2717 }
2718
2719 #[test]
2720 fn set_nx_xx_conflict() {
2721 let err = Command::from_frame(cmd(&["SET", "k", "v", "NX", "XX"])).unwrap_err();
2722 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2723 }
2724
2725 #[test]
2726 fn set_nx_case_insensitive() {
2727 assert_eq!(
2728 Command::from_frame(cmd(&["set", "k", "v", "nx"])).unwrap(),
2729 Command::Set {
2730 key: "k".into(),
2731 value: Bytes::from("v"),
2732 expire: None,
2733 nx: true,
2734 xx: false,
2735 },
2736 );
2737 }
2738
2739 #[test]
2740 fn set_missing_value() {
2741 let err = Command::from_frame(cmd(&["SET", "key"])).unwrap_err();
2742 assert!(matches!(err, ProtocolError::WrongArity(_)));
2743 }
2744
2745 #[test]
2746 fn set_invalid_expire_value() {
2747 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX", "notanum"])).unwrap_err();
2748 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2749 }
2750
2751 #[test]
2752 fn set_zero_expire() {
2753 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX", "0"])).unwrap_err();
2754 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2755 }
2756
2757 #[test]
2758 fn set_unknown_flag() {
2759 let err = Command::from_frame(cmd(&["SET", "k", "v", "ZZ", "10"])).unwrap_err();
2760 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2761 }
2762
2763 #[test]
2764 fn set_incomplete_expire() {
2765 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX"])).unwrap_err();
2767 assert!(matches!(err, ProtocolError::WrongArity(_)));
2768 }
2769
2770 #[test]
2773 fn del_single() {
2774 assert_eq!(
2775 Command::from_frame(cmd(&["DEL", "key"])).unwrap(),
2776 Command::Del {
2777 keys: vec!["key".into()]
2778 },
2779 );
2780 }
2781
2782 #[test]
2783 fn del_multiple() {
2784 assert_eq!(
2785 Command::from_frame(cmd(&["DEL", "a", "b", "c"])).unwrap(),
2786 Command::Del {
2787 keys: vec!["a".into(), "b".into(), "c".into()]
2788 },
2789 );
2790 }
2791
2792 #[test]
2793 fn del_no_args() {
2794 let err = Command::from_frame(cmd(&["DEL"])).unwrap_err();
2795 assert!(matches!(err, ProtocolError::WrongArity(_)));
2796 }
2797
2798 #[test]
2801 fn exists_single() {
2802 assert_eq!(
2803 Command::from_frame(cmd(&["EXISTS", "key"])).unwrap(),
2804 Command::Exists {
2805 keys: vec!["key".into()]
2806 },
2807 );
2808 }
2809
2810 #[test]
2811 fn exists_multiple() {
2812 assert_eq!(
2813 Command::from_frame(cmd(&["EXISTS", "a", "b"])).unwrap(),
2814 Command::Exists {
2815 keys: vec!["a".into(), "b".into()]
2816 },
2817 );
2818 }
2819
2820 #[test]
2821 fn exists_no_args() {
2822 let err = Command::from_frame(cmd(&["EXISTS"])).unwrap_err();
2823 assert!(matches!(err, ProtocolError::WrongArity(_)));
2824 }
2825
2826 #[test]
2829 fn mget_single() {
2830 assert_eq!(
2831 Command::from_frame(cmd(&["MGET", "key"])).unwrap(),
2832 Command::MGet {
2833 keys: vec!["key".into()]
2834 },
2835 );
2836 }
2837
2838 #[test]
2839 fn mget_multiple() {
2840 assert_eq!(
2841 Command::from_frame(cmd(&["MGET", "a", "b", "c"])).unwrap(),
2842 Command::MGet {
2843 keys: vec!["a".into(), "b".into(), "c".into()]
2844 },
2845 );
2846 }
2847
2848 #[test]
2849 fn mget_no_args() {
2850 let err = Command::from_frame(cmd(&["MGET"])).unwrap_err();
2851 assert!(matches!(err, ProtocolError::WrongArity(_)));
2852 }
2853
2854 #[test]
2857 fn mset_single_pair() {
2858 assert_eq!(
2859 Command::from_frame(cmd(&["MSET", "key", "val"])).unwrap(),
2860 Command::MSet {
2861 pairs: vec![("key".into(), Bytes::from("val"))]
2862 },
2863 );
2864 }
2865
2866 #[test]
2867 fn mset_multiple_pairs() {
2868 assert_eq!(
2869 Command::from_frame(cmd(&["MSET", "a", "1", "b", "2"])).unwrap(),
2870 Command::MSet {
2871 pairs: vec![
2872 ("a".into(), Bytes::from("1")),
2873 ("b".into(), Bytes::from("2")),
2874 ]
2875 },
2876 );
2877 }
2878
2879 #[test]
2880 fn mset_no_args() {
2881 let err = Command::from_frame(cmd(&["MSET"])).unwrap_err();
2882 assert!(matches!(err, ProtocolError::WrongArity(_)));
2883 }
2884
2885 #[test]
2886 fn mset_odd_args() {
2887 let err = Command::from_frame(cmd(&["MSET", "a", "1", "b"])).unwrap_err();
2889 assert!(matches!(err, ProtocolError::WrongArity(_)));
2890 }
2891
2892 #[test]
2895 fn expire_basic() {
2896 assert_eq!(
2897 Command::from_frame(cmd(&["EXPIRE", "key", "60"])).unwrap(),
2898 Command::Expire {
2899 key: "key".into(),
2900 seconds: 60,
2901 },
2902 );
2903 }
2904
2905 #[test]
2906 fn expire_wrong_arity() {
2907 let err = Command::from_frame(cmd(&["EXPIRE", "key"])).unwrap_err();
2908 assert!(matches!(err, ProtocolError::WrongArity(_)));
2909 }
2910
2911 #[test]
2912 fn expire_invalid_seconds() {
2913 let err = Command::from_frame(cmd(&["EXPIRE", "key", "abc"])).unwrap_err();
2914 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2915 }
2916
2917 #[test]
2918 fn expire_zero_seconds() {
2919 let err = Command::from_frame(cmd(&["EXPIRE", "key", "0"])).unwrap_err();
2920 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2921 }
2922
2923 #[test]
2926 fn ttl_basic() {
2927 assert_eq!(
2928 Command::from_frame(cmd(&["TTL", "key"])).unwrap(),
2929 Command::Ttl { key: "key".into() },
2930 );
2931 }
2932
2933 #[test]
2934 fn ttl_wrong_arity() {
2935 let err = Command::from_frame(cmd(&["TTL"])).unwrap_err();
2936 assert!(matches!(err, ProtocolError::WrongArity(_)));
2937 }
2938
2939 #[test]
2942 fn dbsize_basic() {
2943 assert_eq!(
2944 Command::from_frame(cmd(&["DBSIZE"])).unwrap(),
2945 Command::DbSize,
2946 );
2947 }
2948
2949 #[test]
2950 fn dbsize_case_insensitive() {
2951 assert_eq!(
2952 Command::from_frame(cmd(&["dbsize"])).unwrap(),
2953 Command::DbSize,
2954 );
2955 }
2956
2957 #[test]
2958 fn dbsize_extra_args() {
2959 let err = Command::from_frame(cmd(&["DBSIZE", "extra"])).unwrap_err();
2960 assert!(matches!(err, ProtocolError::WrongArity(_)));
2961 }
2962
2963 #[test]
2966 fn info_no_section() {
2967 assert_eq!(
2968 Command::from_frame(cmd(&["INFO"])).unwrap(),
2969 Command::Info { section: None },
2970 );
2971 }
2972
2973 #[test]
2974 fn info_with_section() {
2975 assert_eq!(
2976 Command::from_frame(cmd(&["INFO", "keyspace"])).unwrap(),
2977 Command::Info {
2978 section: Some("keyspace".into())
2979 },
2980 );
2981 }
2982
2983 #[test]
2984 fn info_too_many_args() {
2985 let err = Command::from_frame(cmd(&["INFO", "a", "b"])).unwrap_err();
2986 assert!(matches!(err, ProtocolError::WrongArity(_)));
2987 }
2988
2989 #[test]
2992 fn bgsave_basic() {
2993 assert_eq!(
2994 Command::from_frame(cmd(&["BGSAVE"])).unwrap(),
2995 Command::BgSave,
2996 );
2997 }
2998
2999 #[test]
3000 fn bgsave_case_insensitive() {
3001 assert_eq!(
3002 Command::from_frame(cmd(&["bgsave"])).unwrap(),
3003 Command::BgSave,
3004 );
3005 }
3006
3007 #[test]
3008 fn bgsave_extra_args() {
3009 let err = Command::from_frame(cmd(&["BGSAVE", "extra"])).unwrap_err();
3010 assert!(matches!(err, ProtocolError::WrongArity(_)));
3011 }
3012
3013 #[test]
3016 fn bgrewriteaof_basic() {
3017 assert_eq!(
3018 Command::from_frame(cmd(&["BGREWRITEAOF"])).unwrap(),
3019 Command::BgRewriteAof,
3020 );
3021 }
3022
3023 #[test]
3024 fn bgrewriteaof_case_insensitive() {
3025 assert_eq!(
3026 Command::from_frame(cmd(&["bgrewriteaof"])).unwrap(),
3027 Command::BgRewriteAof,
3028 );
3029 }
3030
3031 #[test]
3032 fn bgrewriteaof_extra_args() {
3033 let err = Command::from_frame(cmd(&["BGREWRITEAOF", "extra"])).unwrap_err();
3034 assert!(matches!(err, ProtocolError::WrongArity(_)));
3035 }
3036
3037 #[test]
3040 fn flushdb_basic() {
3041 assert_eq!(
3042 Command::from_frame(cmd(&["FLUSHDB"])).unwrap(),
3043 Command::FlushDb { async_mode: false },
3044 );
3045 }
3046
3047 #[test]
3048 fn flushdb_case_insensitive() {
3049 assert_eq!(
3050 Command::from_frame(cmd(&["flushdb"])).unwrap(),
3051 Command::FlushDb { async_mode: false },
3052 );
3053 }
3054
3055 #[test]
3056 fn flushdb_async() {
3057 assert_eq!(
3058 Command::from_frame(cmd(&["FLUSHDB", "ASYNC"])).unwrap(),
3059 Command::FlushDb { async_mode: true },
3060 );
3061 }
3062
3063 #[test]
3064 fn flushdb_async_case_insensitive() {
3065 assert_eq!(
3066 Command::from_frame(cmd(&["flushdb", "async"])).unwrap(),
3067 Command::FlushDb { async_mode: true },
3068 );
3069 }
3070
3071 #[test]
3072 fn flushdb_extra_args() {
3073 let err = Command::from_frame(cmd(&["FLUSHDB", "extra"])).unwrap_err();
3074 assert!(matches!(err, ProtocolError::WrongArity(_)));
3075 }
3076
3077 #[test]
3080 fn unlink_single() {
3081 assert_eq!(
3082 Command::from_frame(cmd(&["UNLINK", "mykey"])).unwrap(),
3083 Command::Unlink {
3084 keys: vec!["mykey".into()]
3085 },
3086 );
3087 }
3088
3089 #[test]
3090 fn unlink_multiple() {
3091 assert_eq!(
3092 Command::from_frame(cmd(&["UNLINK", "a", "b", "c"])).unwrap(),
3093 Command::Unlink {
3094 keys: vec!["a".into(), "b".into(), "c".into()]
3095 },
3096 );
3097 }
3098
3099 #[test]
3100 fn unlink_no_args() {
3101 let err = Command::from_frame(cmd(&["UNLINK"])).unwrap_err();
3102 assert!(matches!(err, ProtocolError::WrongArity(_)));
3103 }
3104
3105 #[test]
3108 fn lpush_single() {
3109 assert_eq!(
3110 Command::from_frame(cmd(&["LPUSH", "list", "val"])).unwrap(),
3111 Command::LPush {
3112 key: "list".into(),
3113 values: vec![Bytes::from("val")],
3114 },
3115 );
3116 }
3117
3118 #[test]
3119 fn lpush_multiple() {
3120 assert_eq!(
3121 Command::from_frame(cmd(&["LPUSH", "list", "a", "b", "c"])).unwrap(),
3122 Command::LPush {
3123 key: "list".into(),
3124 values: vec![Bytes::from("a"), Bytes::from("b"), Bytes::from("c")],
3125 },
3126 );
3127 }
3128
3129 #[test]
3130 fn lpush_no_value() {
3131 let err = Command::from_frame(cmd(&["LPUSH", "key"])).unwrap_err();
3132 assert!(matches!(err, ProtocolError::WrongArity(_)));
3133 }
3134
3135 #[test]
3136 fn lpush_case_insensitive() {
3137 assert!(matches!(
3138 Command::from_frame(cmd(&["lpush", "k", "v"])).unwrap(),
3139 Command::LPush { .. }
3140 ));
3141 }
3142
3143 #[test]
3146 fn rpush_single() {
3147 assert_eq!(
3148 Command::from_frame(cmd(&["RPUSH", "list", "val"])).unwrap(),
3149 Command::RPush {
3150 key: "list".into(),
3151 values: vec![Bytes::from("val")],
3152 },
3153 );
3154 }
3155
3156 #[test]
3157 fn rpush_no_value() {
3158 let err = Command::from_frame(cmd(&["RPUSH", "key"])).unwrap_err();
3159 assert!(matches!(err, ProtocolError::WrongArity(_)));
3160 }
3161
3162 #[test]
3165 fn lpop_basic() {
3166 assert_eq!(
3167 Command::from_frame(cmd(&["LPOP", "list"])).unwrap(),
3168 Command::LPop { key: "list".into() },
3169 );
3170 }
3171
3172 #[test]
3173 fn lpop_wrong_arity() {
3174 let err = Command::from_frame(cmd(&["LPOP"])).unwrap_err();
3175 assert!(matches!(err, ProtocolError::WrongArity(_)));
3176 }
3177
3178 #[test]
3181 fn rpop_basic() {
3182 assert_eq!(
3183 Command::from_frame(cmd(&["RPOP", "list"])).unwrap(),
3184 Command::RPop { key: "list".into() },
3185 );
3186 }
3187
3188 #[test]
3191 fn lrange_basic() {
3192 assert_eq!(
3193 Command::from_frame(cmd(&["LRANGE", "list", "0", "-1"])).unwrap(),
3194 Command::LRange {
3195 key: "list".into(),
3196 start: 0,
3197 stop: -1,
3198 },
3199 );
3200 }
3201
3202 #[test]
3203 fn lrange_wrong_arity() {
3204 let err = Command::from_frame(cmd(&["LRANGE", "list", "0"])).unwrap_err();
3205 assert!(matches!(err, ProtocolError::WrongArity(_)));
3206 }
3207
3208 #[test]
3209 fn lrange_invalid_index() {
3210 let err = Command::from_frame(cmd(&["LRANGE", "list", "abc", "0"])).unwrap_err();
3211 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3212 }
3213
3214 #[test]
3217 fn llen_basic() {
3218 assert_eq!(
3219 Command::from_frame(cmd(&["LLEN", "list"])).unwrap(),
3220 Command::LLen { key: "list".into() },
3221 );
3222 }
3223
3224 #[test]
3225 fn llen_wrong_arity() {
3226 let err = Command::from_frame(cmd(&["LLEN"])).unwrap_err();
3227 assert!(matches!(err, ProtocolError::WrongArity(_)));
3228 }
3229
3230 #[test]
3233 fn type_basic() {
3234 assert_eq!(
3235 Command::from_frame(cmd(&["TYPE", "key"])).unwrap(),
3236 Command::Type { key: "key".into() },
3237 );
3238 }
3239
3240 #[test]
3241 fn type_wrong_arity() {
3242 let err = Command::from_frame(cmd(&["TYPE"])).unwrap_err();
3243 assert!(matches!(err, ProtocolError::WrongArity(_)));
3244 }
3245
3246 #[test]
3247 fn type_case_insensitive() {
3248 assert!(matches!(
3249 Command::from_frame(cmd(&["type", "k"])).unwrap(),
3250 Command::Type { .. }
3251 ));
3252 }
3253
3254 #[test]
3257 fn unknown_command() {
3258 assert_eq!(
3259 Command::from_frame(cmd(&["FOOBAR", "arg"])).unwrap(),
3260 Command::Unknown("FOOBAR".into()),
3261 );
3262 }
3263
3264 #[test]
3265 fn non_array_frame() {
3266 let err = Command::from_frame(Frame::Simple("PING".into())).unwrap_err();
3267 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3268 }
3269
3270 #[test]
3271 fn empty_array() {
3272 let err = Command::from_frame(Frame::Array(vec![])).unwrap_err();
3273 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3274 }
3275
3276 #[test]
3279 fn zadd_basic() {
3280 let parsed = Command::from_frame(cmd(&["ZADD", "board", "100", "alice"])).unwrap();
3281 match parsed {
3282 Command::ZAdd {
3283 key,
3284 flags,
3285 members,
3286 } => {
3287 assert_eq!(key, "board");
3288 assert_eq!(flags, ZAddFlags::default());
3289 assert_eq!(members, vec![(100.0, "alice".into())]);
3290 }
3291 other => panic!("expected ZAdd, got {other:?}"),
3292 }
3293 }
3294
3295 #[test]
3296 fn zadd_multiple_members() {
3297 let parsed =
3298 Command::from_frame(cmd(&["ZADD", "board", "100", "alice", "200", "bob"])).unwrap();
3299 match parsed {
3300 Command::ZAdd { members, .. } => {
3301 assert_eq!(members.len(), 2);
3302 assert_eq!(members[0], (100.0, "alice".into()));
3303 assert_eq!(members[1], (200.0, "bob".into()));
3304 }
3305 other => panic!("expected ZAdd, got {other:?}"),
3306 }
3307 }
3308
3309 #[test]
3310 fn zadd_with_flags() {
3311 let parsed = Command::from_frame(cmd(&["ZADD", "z", "NX", "CH", "100", "alice"])).unwrap();
3312 match parsed {
3313 Command::ZAdd { flags, .. } => {
3314 assert!(flags.nx);
3315 assert!(flags.ch);
3316 assert!(!flags.xx);
3317 assert!(!flags.gt);
3318 assert!(!flags.lt);
3319 }
3320 other => panic!("expected ZAdd, got {other:?}"),
3321 }
3322 }
3323
3324 #[test]
3325 fn zadd_gt_flag() {
3326 let parsed = Command::from_frame(cmd(&["zadd", "z", "gt", "100", "alice"])).unwrap();
3327 match parsed {
3328 Command::ZAdd { flags, .. } => assert!(flags.gt),
3329 other => panic!("expected ZAdd, got {other:?}"),
3330 }
3331 }
3332
3333 #[test]
3334 fn zadd_nx_xx_conflict() {
3335 let err = Command::from_frame(cmd(&["ZADD", "z", "NX", "XX", "100", "alice"])).unwrap_err();
3336 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3337 }
3338
3339 #[test]
3340 fn zadd_gt_lt_conflict() {
3341 let err = Command::from_frame(cmd(&["ZADD", "z", "GT", "LT", "100", "alice"])).unwrap_err();
3342 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3343 }
3344
3345 #[test]
3346 fn zadd_wrong_arity() {
3347 let err = Command::from_frame(cmd(&["ZADD", "z"])).unwrap_err();
3348 assert!(matches!(err, ProtocolError::WrongArity(_)));
3349 }
3350
3351 #[test]
3352 fn zadd_odd_score_member_count() {
3353 let err = Command::from_frame(cmd(&["ZADD", "z", "100"])).unwrap_err();
3355 assert!(matches!(err, ProtocolError::WrongArity(_)));
3356 }
3357
3358 #[test]
3359 fn zadd_invalid_score() {
3360 let err = Command::from_frame(cmd(&["ZADD", "z", "notanum", "alice"])).unwrap_err();
3361 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3362 }
3363
3364 #[test]
3365 fn zadd_nan_score() {
3366 let err = Command::from_frame(cmd(&["ZADD", "z", "nan", "alice"])).unwrap_err();
3367 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3368 }
3369
3370 #[test]
3371 fn zadd_negative_score() {
3372 let parsed = Command::from_frame(cmd(&["ZADD", "z", "-50.5", "alice"])).unwrap();
3373 match parsed {
3374 Command::ZAdd { members, .. } => {
3375 assert_eq!(members[0].0, -50.5);
3376 }
3377 other => panic!("expected ZAdd, got {other:?}"),
3378 }
3379 }
3380
3381 #[test]
3384 fn zrem_basic() {
3385 assert_eq!(
3386 Command::from_frame(cmd(&["ZREM", "z", "alice"])).unwrap(),
3387 Command::ZRem {
3388 key: "z".into(),
3389 members: vec!["alice".into()],
3390 },
3391 );
3392 }
3393
3394 #[test]
3395 fn zrem_multiple() {
3396 let parsed = Command::from_frame(cmd(&["ZREM", "z", "a", "b", "c"])).unwrap();
3397 match parsed {
3398 Command::ZRem { members, .. } => assert_eq!(members.len(), 3),
3399 other => panic!("expected ZRem, got {other:?}"),
3400 }
3401 }
3402
3403 #[test]
3404 fn zrem_wrong_arity() {
3405 let err = Command::from_frame(cmd(&["ZREM", "z"])).unwrap_err();
3406 assert!(matches!(err, ProtocolError::WrongArity(_)));
3407 }
3408
3409 #[test]
3412 fn zscore_basic() {
3413 assert_eq!(
3414 Command::from_frame(cmd(&["ZSCORE", "z", "alice"])).unwrap(),
3415 Command::ZScore {
3416 key: "z".into(),
3417 member: "alice".into(),
3418 },
3419 );
3420 }
3421
3422 #[test]
3423 fn zscore_wrong_arity() {
3424 let err = Command::from_frame(cmd(&["ZSCORE", "z"])).unwrap_err();
3425 assert!(matches!(err, ProtocolError::WrongArity(_)));
3426 }
3427
3428 #[test]
3431 fn zrank_basic() {
3432 assert_eq!(
3433 Command::from_frame(cmd(&["ZRANK", "z", "alice"])).unwrap(),
3434 Command::ZRank {
3435 key: "z".into(),
3436 member: "alice".into(),
3437 },
3438 );
3439 }
3440
3441 #[test]
3442 fn zrank_wrong_arity() {
3443 let err = Command::from_frame(cmd(&["ZRANK", "z"])).unwrap_err();
3444 assert!(matches!(err, ProtocolError::WrongArity(_)));
3445 }
3446
3447 #[test]
3450 fn zcard_basic() {
3451 assert_eq!(
3452 Command::from_frame(cmd(&["ZCARD", "z"])).unwrap(),
3453 Command::ZCard { key: "z".into() },
3454 );
3455 }
3456
3457 #[test]
3458 fn zcard_wrong_arity() {
3459 let err = Command::from_frame(cmd(&["ZCARD"])).unwrap_err();
3460 assert!(matches!(err, ProtocolError::WrongArity(_)));
3461 }
3462
3463 #[test]
3466 fn zrange_basic() {
3467 assert_eq!(
3468 Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1"])).unwrap(),
3469 Command::ZRange {
3470 key: "z".into(),
3471 start: 0,
3472 stop: -1,
3473 with_scores: false,
3474 },
3475 );
3476 }
3477
3478 #[test]
3479 fn zrange_with_scores() {
3480 assert_eq!(
3481 Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1", "WITHSCORES"])).unwrap(),
3482 Command::ZRange {
3483 key: "z".into(),
3484 start: 0,
3485 stop: -1,
3486 with_scores: true,
3487 },
3488 );
3489 }
3490
3491 #[test]
3492 fn zrange_withscores_case_insensitive() {
3493 assert_eq!(
3494 Command::from_frame(cmd(&["zrange", "z", "0", "-1", "withscores"])).unwrap(),
3495 Command::ZRange {
3496 key: "z".into(),
3497 start: 0,
3498 stop: -1,
3499 with_scores: true,
3500 },
3501 );
3502 }
3503
3504 #[test]
3505 fn zrange_invalid_option() {
3506 let err = Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1", "BADOPT"])).unwrap_err();
3507 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3508 }
3509
3510 #[test]
3511 fn zrange_wrong_arity() {
3512 let err = Command::from_frame(cmd(&["ZRANGE", "z", "0"])).unwrap_err();
3513 assert!(matches!(err, ProtocolError::WrongArity(_)));
3514 }
3515
3516 #[test]
3519 fn incr_basic() {
3520 assert_eq!(
3521 Command::from_frame(cmd(&["INCR", "counter"])).unwrap(),
3522 Command::Incr {
3523 key: "counter".into()
3524 },
3525 );
3526 }
3527
3528 #[test]
3529 fn incr_wrong_arity() {
3530 let err = Command::from_frame(cmd(&["INCR"])).unwrap_err();
3531 assert!(matches!(err, ProtocolError::WrongArity(_)));
3532 }
3533
3534 #[test]
3537 fn decr_basic() {
3538 assert_eq!(
3539 Command::from_frame(cmd(&["DECR", "counter"])).unwrap(),
3540 Command::Decr {
3541 key: "counter".into()
3542 },
3543 );
3544 }
3545
3546 #[test]
3547 fn decr_wrong_arity() {
3548 let err = Command::from_frame(cmd(&["DECR"])).unwrap_err();
3549 assert!(matches!(err, ProtocolError::WrongArity(_)));
3550 }
3551
3552 #[test]
3555 fn persist_basic() {
3556 assert_eq!(
3557 Command::from_frame(cmd(&["PERSIST", "key"])).unwrap(),
3558 Command::Persist { key: "key".into() },
3559 );
3560 }
3561
3562 #[test]
3563 fn persist_case_insensitive() {
3564 assert_eq!(
3565 Command::from_frame(cmd(&["persist", "key"])).unwrap(),
3566 Command::Persist { key: "key".into() },
3567 );
3568 }
3569
3570 #[test]
3571 fn persist_wrong_arity() {
3572 let err = Command::from_frame(cmd(&["PERSIST"])).unwrap_err();
3573 assert!(matches!(err, ProtocolError::WrongArity(_)));
3574 }
3575
3576 #[test]
3579 fn pttl_basic() {
3580 assert_eq!(
3581 Command::from_frame(cmd(&["PTTL", "key"])).unwrap(),
3582 Command::Pttl { key: "key".into() },
3583 );
3584 }
3585
3586 #[test]
3587 fn pttl_wrong_arity() {
3588 let err = Command::from_frame(cmd(&["PTTL"])).unwrap_err();
3589 assert!(matches!(err, ProtocolError::WrongArity(_)));
3590 }
3591
3592 #[test]
3595 fn pexpire_basic() {
3596 assert_eq!(
3597 Command::from_frame(cmd(&["PEXPIRE", "key", "5000"])).unwrap(),
3598 Command::Pexpire {
3599 key: "key".into(),
3600 milliseconds: 5000,
3601 },
3602 );
3603 }
3604
3605 #[test]
3606 fn pexpire_wrong_arity() {
3607 let err = Command::from_frame(cmd(&["PEXPIRE", "key"])).unwrap_err();
3608 assert!(matches!(err, ProtocolError::WrongArity(_)));
3609 }
3610
3611 #[test]
3612 fn pexpire_zero_millis() {
3613 let err = Command::from_frame(cmd(&["PEXPIRE", "key", "0"])).unwrap_err();
3614 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3615 }
3616
3617 #[test]
3618 fn pexpire_invalid_millis() {
3619 let err = Command::from_frame(cmd(&["PEXPIRE", "key", "notanum"])).unwrap_err();
3620 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3621 }
3622
3623 #[test]
3626 fn scan_basic() {
3627 assert_eq!(
3628 Command::from_frame(cmd(&["SCAN", "0"])).unwrap(),
3629 Command::Scan {
3630 cursor: 0,
3631 pattern: None,
3632 count: None,
3633 },
3634 );
3635 }
3636
3637 #[test]
3638 fn scan_with_match() {
3639 assert_eq!(
3640 Command::from_frame(cmd(&["SCAN", "0", "MATCH", "user:*"])).unwrap(),
3641 Command::Scan {
3642 cursor: 0,
3643 pattern: Some("user:*".into()),
3644 count: None,
3645 },
3646 );
3647 }
3648
3649 #[test]
3650 fn scan_with_count() {
3651 assert_eq!(
3652 Command::from_frame(cmd(&["SCAN", "42", "COUNT", "100"])).unwrap(),
3653 Command::Scan {
3654 cursor: 42,
3655 pattern: None,
3656 count: Some(100),
3657 },
3658 );
3659 }
3660
3661 #[test]
3662 fn scan_with_match_and_count() {
3663 assert_eq!(
3664 Command::from_frame(cmd(&["SCAN", "0", "MATCH", "*:data", "COUNT", "50"])).unwrap(),
3665 Command::Scan {
3666 cursor: 0,
3667 pattern: Some("*:data".into()),
3668 count: Some(50),
3669 },
3670 );
3671 }
3672
3673 #[test]
3674 fn scan_count_before_match() {
3675 assert_eq!(
3676 Command::from_frame(cmd(&["SCAN", "0", "COUNT", "10", "MATCH", "foo*"])).unwrap(),
3677 Command::Scan {
3678 cursor: 0,
3679 pattern: Some("foo*".into()),
3680 count: Some(10),
3681 },
3682 );
3683 }
3684
3685 #[test]
3686 fn scan_case_insensitive() {
3687 assert_eq!(
3688 Command::from_frame(cmd(&["scan", "0", "match", "x*", "count", "5"])).unwrap(),
3689 Command::Scan {
3690 cursor: 0,
3691 pattern: Some("x*".into()),
3692 count: Some(5),
3693 },
3694 );
3695 }
3696
3697 #[test]
3698 fn scan_wrong_arity() {
3699 let err = Command::from_frame(cmd(&["SCAN"])).unwrap_err();
3700 assert!(matches!(err, ProtocolError::WrongArity(_)));
3701 }
3702
3703 #[test]
3704 fn scan_invalid_cursor() {
3705 let err = Command::from_frame(cmd(&["SCAN", "notanum"])).unwrap_err();
3706 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3707 }
3708
3709 #[test]
3710 fn scan_invalid_count() {
3711 let err = Command::from_frame(cmd(&["SCAN", "0", "COUNT", "bad"])).unwrap_err();
3712 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3713 }
3714
3715 #[test]
3716 fn scan_unknown_flag() {
3717 let err = Command::from_frame(cmd(&["SCAN", "0", "BADOPT", "val"])).unwrap_err();
3718 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
3719 }
3720
3721 #[test]
3722 fn scan_match_missing_pattern() {
3723 let err = Command::from_frame(cmd(&["SCAN", "0", "MATCH"])).unwrap_err();
3724 assert!(matches!(err, ProtocolError::WrongArity(_)));
3725 }
3726
3727 #[test]
3728 fn scan_count_missing_value() {
3729 let err = Command::from_frame(cmd(&["SCAN", "0", "COUNT"])).unwrap_err();
3730 assert!(matches!(err, ProtocolError::WrongArity(_)));
3731 }
3732
3733 #[test]
3736 fn hset_single_field() {
3737 assert_eq!(
3738 Command::from_frame(cmd(&["HSET", "h", "field", "value"])).unwrap(),
3739 Command::HSet {
3740 key: "h".into(),
3741 fields: vec![("field".into(), Bytes::from("value"))],
3742 },
3743 );
3744 }
3745
3746 #[test]
3747 fn hset_multiple_fields() {
3748 let parsed = Command::from_frame(cmd(&["HSET", "h", "f1", "v1", "f2", "v2"])).unwrap();
3749 match parsed {
3750 Command::HSet { key, fields } => {
3751 assert_eq!(key, "h");
3752 assert_eq!(fields.len(), 2);
3753 }
3754 other => panic!("expected HSet, got {other:?}"),
3755 }
3756 }
3757
3758 #[test]
3759 fn hset_wrong_arity() {
3760 let err = Command::from_frame(cmd(&["HSET", "h"])).unwrap_err();
3761 assert!(matches!(err, ProtocolError::WrongArity(_)));
3762 let err = Command::from_frame(cmd(&["HSET", "h", "f"])).unwrap_err();
3763 assert!(matches!(err, ProtocolError::WrongArity(_)));
3764 }
3765
3766 #[test]
3767 fn hget_basic() {
3768 assert_eq!(
3769 Command::from_frame(cmd(&["HGET", "h", "field"])).unwrap(),
3770 Command::HGet {
3771 key: "h".into(),
3772 field: "field".into(),
3773 },
3774 );
3775 }
3776
3777 #[test]
3778 fn hget_wrong_arity() {
3779 let err = Command::from_frame(cmd(&["HGET", "h"])).unwrap_err();
3780 assert!(matches!(err, ProtocolError::WrongArity(_)));
3781 }
3782
3783 #[test]
3784 fn hgetall_basic() {
3785 assert_eq!(
3786 Command::from_frame(cmd(&["HGETALL", "h"])).unwrap(),
3787 Command::HGetAll { key: "h".into() },
3788 );
3789 }
3790
3791 #[test]
3792 fn hgetall_wrong_arity() {
3793 let err = Command::from_frame(cmd(&["HGETALL"])).unwrap_err();
3794 assert!(matches!(err, ProtocolError::WrongArity(_)));
3795 }
3796
3797 #[test]
3798 fn hdel_single() {
3799 assert_eq!(
3800 Command::from_frame(cmd(&["HDEL", "h", "f"])).unwrap(),
3801 Command::HDel {
3802 key: "h".into(),
3803 fields: vec!["f".into()],
3804 },
3805 );
3806 }
3807
3808 #[test]
3809 fn hdel_multiple() {
3810 let parsed = Command::from_frame(cmd(&["HDEL", "h", "f1", "f2", "f3"])).unwrap();
3811 match parsed {
3812 Command::HDel { fields, .. } => assert_eq!(fields.len(), 3),
3813 other => panic!("expected HDel, got {other:?}"),
3814 }
3815 }
3816
3817 #[test]
3818 fn hdel_wrong_arity() {
3819 let err = Command::from_frame(cmd(&["HDEL", "h"])).unwrap_err();
3820 assert!(matches!(err, ProtocolError::WrongArity(_)));
3821 }
3822
3823 #[test]
3824 fn hexists_basic() {
3825 assert_eq!(
3826 Command::from_frame(cmd(&["HEXISTS", "h", "f"])).unwrap(),
3827 Command::HExists {
3828 key: "h".into(),
3829 field: "f".into(),
3830 },
3831 );
3832 }
3833
3834 #[test]
3835 fn hlen_basic() {
3836 assert_eq!(
3837 Command::from_frame(cmd(&["HLEN", "h"])).unwrap(),
3838 Command::HLen { key: "h".into() },
3839 );
3840 }
3841
3842 #[test]
3843 fn hincrby_basic() {
3844 assert_eq!(
3845 Command::from_frame(cmd(&["HINCRBY", "h", "f", "5"])).unwrap(),
3846 Command::HIncrBy {
3847 key: "h".into(),
3848 field: "f".into(),
3849 delta: 5,
3850 },
3851 );
3852 }
3853
3854 #[test]
3855 fn hincrby_negative() {
3856 assert_eq!(
3857 Command::from_frame(cmd(&["HINCRBY", "h", "f", "-3"])).unwrap(),
3858 Command::HIncrBy {
3859 key: "h".into(),
3860 field: "f".into(),
3861 delta: -3,
3862 },
3863 );
3864 }
3865
3866 #[test]
3867 fn hincrby_wrong_arity() {
3868 let err = Command::from_frame(cmd(&["HINCRBY", "h", "f"])).unwrap_err();
3869 assert!(matches!(err, ProtocolError::WrongArity(_)));
3870 }
3871
3872 #[test]
3873 fn hkeys_basic() {
3874 assert_eq!(
3875 Command::from_frame(cmd(&["HKEYS", "h"])).unwrap(),
3876 Command::HKeys { key: "h".into() },
3877 );
3878 }
3879
3880 #[test]
3881 fn hvals_basic() {
3882 assert_eq!(
3883 Command::from_frame(cmd(&["HVALS", "h"])).unwrap(),
3884 Command::HVals { key: "h".into() },
3885 );
3886 }
3887
3888 #[test]
3889 fn hmget_basic() {
3890 assert_eq!(
3891 Command::from_frame(cmd(&["HMGET", "h", "f1", "f2"])).unwrap(),
3892 Command::HMGet {
3893 key: "h".into(),
3894 fields: vec!["f1".into(), "f2".into()],
3895 },
3896 );
3897 }
3898
3899 #[test]
3900 fn hmget_wrong_arity() {
3901 let err = Command::from_frame(cmd(&["HMGET", "h"])).unwrap_err();
3902 assert!(matches!(err, ProtocolError::WrongArity(_)));
3903 }
3904
3905 #[test]
3906 fn hash_commands_case_insensitive() {
3907 assert!(matches!(
3908 Command::from_frame(cmd(&["hset", "h", "f", "v"])).unwrap(),
3909 Command::HSet { .. }
3910 ));
3911 assert!(matches!(
3912 Command::from_frame(cmd(&["hget", "h", "f"])).unwrap(),
3913 Command::HGet { .. }
3914 ));
3915 assert!(matches!(
3916 Command::from_frame(cmd(&["hgetall", "h"])).unwrap(),
3917 Command::HGetAll { .. }
3918 ));
3919 }
3920
3921 #[test]
3924 fn sadd_single_member() {
3925 assert_eq!(
3926 Command::from_frame(cmd(&["SADD", "s", "member"])).unwrap(),
3927 Command::SAdd {
3928 key: "s".into(),
3929 members: vec!["member".into()],
3930 },
3931 );
3932 }
3933
3934 #[test]
3935 fn sadd_multiple_members() {
3936 let parsed = Command::from_frame(cmd(&["SADD", "s", "a", "b", "c"])).unwrap();
3937 match parsed {
3938 Command::SAdd { key, members } => {
3939 assert_eq!(key, "s");
3940 assert_eq!(members.len(), 3);
3941 }
3942 other => panic!("expected SAdd, got {other:?}"),
3943 }
3944 }
3945
3946 #[test]
3947 fn sadd_wrong_arity() {
3948 let err = Command::from_frame(cmd(&["SADD", "s"])).unwrap_err();
3949 assert!(matches!(err, ProtocolError::WrongArity(_)));
3950 }
3951
3952 #[test]
3953 fn srem_single_member() {
3954 assert_eq!(
3955 Command::from_frame(cmd(&["SREM", "s", "member"])).unwrap(),
3956 Command::SRem {
3957 key: "s".into(),
3958 members: vec!["member".into()],
3959 },
3960 );
3961 }
3962
3963 #[test]
3964 fn srem_multiple_members() {
3965 let parsed = Command::from_frame(cmd(&["SREM", "s", "a", "b"])).unwrap();
3966 match parsed {
3967 Command::SRem { key, members } => {
3968 assert_eq!(key, "s");
3969 assert_eq!(members.len(), 2);
3970 }
3971 other => panic!("expected SRem, got {other:?}"),
3972 }
3973 }
3974
3975 #[test]
3976 fn srem_wrong_arity() {
3977 let err = Command::from_frame(cmd(&["SREM", "s"])).unwrap_err();
3978 assert!(matches!(err, ProtocolError::WrongArity(_)));
3979 }
3980
3981 #[test]
3982 fn smembers_basic() {
3983 assert_eq!(
3984 Command::from_frame(cmd(&["SMEMBERS", "s"])).unwrap(),
3985 Command::SMembers { key: "s".into() },
3986 );
3987 }
3988
3989 #[test]
3990 fn smembers_wrong_arity() {
3991 let err = Command::from_frame(cmd(&["SMEMBERS"])).unwrap_err();
3992 assert!(matches!(err, ProtocolError::WrongArity(_)));
3993 }
3994
3995 #[test]
3996 fn sismember_basic() {
3997 assert_eq!(
3998 Command::from_frame(cmd(&["SISMEMBER", "s", "member"])).unwrap(),
3999 Command::SIsMember {
4000 key: "s".into(),
4001 member: "member".into(),
4002 },
4003 );
4004 }
4005
4006 #[test]
4007 fn sismember_wrong_arity() {
4008 let err = Command::from_frame(cmd(&["SISMEMBER", "s"])).unwrap_err();
4009 assert!(matches!(err, ProtocolError::WrongArity(_)));
4010 }
4011
4012 #[test]
4013 fn scard_basic() {
4014 assert_eq!(
4015 Command::from_frame(cmd(&["SCARD", "s"])).unwrap(),
4016 Command::SCard { key: "s".into() },
4017 );
4018 }
4019
4020 #[test]
4021 fn scard_wrong_arity() {
4022 let err = Command::from_frame(cmd(&["SCARD"])).unwrap_err();
4023 assert!(matches!(err, ProtocolError::WrongArity(_)));
4024 }
4025
4026 #[test]
4027 fn set_commands_case_insensitive() {
4028 assert!(matches!(
4029 Command::from_frame(cmd(&["sadd", "s", "m"])).unwrap(),
4030 Command::SAdd { .. }
4031 ));
4032 assert!(matches!(
4033 Command::from_frame(cmd(&["srem", "s", "m"])).unwrap(),
4034 Command::SRem { .. }
4035 ));
4036 assert!(matches!(
4037 Command::from_frame(cmd(&["smembers", "s"])).unwrap(),
4038 Command::SMembers { .. }
4039 ));
4040 }
4041
4042 #[test]
4045 fn cluster_info_basic() {
4046 assert_eq!(
4047 Command::from_frame(cmd(&["CLUSTER", "INFO"])).unwrap(),
4048 Command::ClusterInfo,
4049 );
4050 }
4051
4052 #[test]
4053 fn cluster_nodes_basic() {
4054 assert_eq!(
4055 Command::from_frame(cmd(&["CLUSTER", "NODES"])).unwrap(),
4056 Command::ClusterNodes,
4057 );
4058 }
4059
4060 #[test]
4061 fn cluster_slots_basic() {
4062 assert_eq!(
4063 Command::from_frame(cmd(&["CLUSTER", "SLOTS"])).unwrap(),
4064 Command::ClusterSlots,
4065 );
4066 }
4067
4068 #[test]
4069 fn cluster_keyslot_basic() {
4070 assert_eq!(
4071 Command::from_frame(cmd(&["CLUSTER", "KEYSLOT", "mykey"])).unwrap(),
4072 Command::ClusterKeySlot {
4073 key: "mykey".into()
4074 },
4075 );
4076 }
4077
4078 #[test]
4079 fn cluster_keyslot_wrong_arity() {
4080 let err = Command::from_frame(cmd(&["CLUSTER", "KEYSLOT"])).unwrap_err();
4081 assert!(matches!(err, ProtocolError::WrongArity(_)));
4082 }
4083
4084 #[test]
4085 fn cluster_myid_basic() {
4086 assert_eq!(
4087 Command::from_frame(cmd(&["CLUSTER", "MYID"])).unwrap(),
4088 Command::ClusterMyId,
4089 );
4090 }
4091
4092 #[test]
4093 fn cluster_unknown_subcommand() {
4094 let err = Command::from_frame(cmd(&["CLUSTER", "BADCMD"])).unwrap_err();
4095 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
4096 }
4097
4098 #[test]
4099 fn cluster_no_subcommand() {
4100 let err = Command::from_frame(cmd(&["CLUSTER"])).unwrap_err();
4101 assert!(matches!(err, ProtocolError::WrongArity(_)));
4102 }
4103
4104 #[test]
4105 fn cluster_case_insensitive() {
4106 assert!(matches!(
4107 Command::from_frame(cmd(&["cluster", "info"])).unwrap(),
4108 Command::ClusterInfo
4109 ));
4110 assert!(matches!(
4111 Command::from_frame(cmd(&["cluster", "keyslot", "k"])).unwrap(),
4112 Command::ClusterKeySlot { .. }
4113 ));
4114 }
4115
4116 #[test]
4117 fn asking_basic() {
4118 assert_eq!(
4119 Command::from_frame(cmd(&["ASKING"])).unwrap(),
4120 Command::Asking,
4121 );
4122 }
4123
4124 #[test]
4125 fn asking_wrong_arity() {
4126 let err = Command::from_frame(cmd(&["ASKING", "extra"])).unwrap_err();
4127 assert!(matches!(err, ProtocolError::WrongArity(_)));
4128 }
4129
4130 #[test]
4131 fn cluster_setslot_importing() {
4132 assert_eq!(
4133 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "100", "IMPORTING", "node123"]))
4134 .unwrap(),
4135 Command::ClusterSetSlotImporting {
4136 slot: 100,
4137 node_id: "node123".into()
4138 },
4139 );
4140 }
4141
4142 #[test]
4143 fn cluster_setslot_migrating() {
4144 assert_eq!(
4145 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "200", "MIGRATING", "node456"]))
4146 .unwrap(),
4147 Command::ClusterSetSlotMigrating {
4148 slot: 200,
4149 node_id: "node456".into()
4150 },
4151 );
4152 }
4153
4154 #[test]
4155 fn cluster_setslot_node() {
4156 assert_eq!(
4157 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "300", "NODE", "node789"])).unwrap(),
4158 Command::ClusterSetSlotNode {
4159 slot: 300,
4160 node_id: "node789".into()
4161 },
4162 );
4163 }
4164
4165 #[test]
4166 fn cluster_setslot_stable() {
4167 assert_eq!(
4168 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "400", "STABLE"])).unwrap(),
4169 Command::ClusterSetSlotStable { slot: 400 },
4170 );
4171 }
4172
4173 #[test]
4174 fn cluster_setslot_invalid_slot() {
4175 let err =
4176 Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "notanumber", "STABLE"])).unwrap_err();
4177 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
4178 }
4179
4180 #[test]
4181 fn cluster_setslot_wrong_arity() {
4182 let err = Command::from_frame(cmd(&["CLUSTER", "SETSLOT", "100"])).unwrap_err();
4183 assert!(matches!(err, ProtocolError::WrongArity(_)));
4184 }
4185
4186 #[test]
4187 fn migrate_basic() {
4188 assert_eq!(
4189 Command::from_frame(cmd(&["MIGRATE", "127.0.0.1", "6379", "mykey", "0", "5000"]))
4190 .unwrap(),
4191 Command::Migrate {
4192 host: "127.0.0.1".into(),
4193 port: 6379,
4194 key: "mykey".into(),
4195 db: 0,
4196 timeout_ms: 5000,
4197 copy: false,
4198 replace: false,
4199 },
4200 );
4201 }
4202
4203 #[test]
4204 fn migrate_with_options() {
4205 assert_eq!(
4206 Command::from_frame(cmd(&[
4207 "MIGRATE",
4208 "192.168.1.1",
4209 "6380",
4210 "testkey",
4211 "1",
4212 "10000",
4213 "COPY",
4214 "REPLACE"
4215 ]))
4216 .unwrap(),
4217 Command::Migrate {
4218 host: "192.168.1.1".into(),
4219 port: 6380,
4220 key: "testkey".into(),
4221 db: 1,
4222 timeout_ms: 10000,
4223 copy: true,
4224 replace: true,
4225 },
4226 );
4227 }
4228
4229 #[test]
4230 fn migrate_wrong_arity() {
4231 let err = Command::from_frame(cmd(&["MIGRATE", "host", "port", "key"])).unwrap_err();
4232 assert!(matches!(err, ProtocolError::WrongArity(_)));
4233 }
4234
4235 #[test]
4236 fn migrate_invalid_port() {
4237 let err = Command::from_frame(cmd(&["MIGRATE", "host", "notaport", "key", "0", "1000"]))
4238 .unwrap_err();
4239 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
4240 }
4241
4242 #[test]
4243 fn cluster_meet_basic() {
4244 assert_eq!(
4245 Command::from_frame(cmd(&["CLUSTER", "MEET", "192.168.1.1", "6379"])).unwrap(),
4246 Command::ClusterMeet {
4247 ip: "192.168.1.1".into(),
4248 port: 6379
4249 },
4250 );
4251 }
4252
4253 #[test]
4254 fn cluster_addslots_basic() {
4255 assert_eq!(
4256 Command::from_frame(cmd(&["CLUSTER", "ADDSLOTS", "0", "1", "2"])).unwrap(),
4257 Command::ClusterAddSlots {
4258 slots: vec![0, 1, 2]
4259 },
4260 );
4261 }
4262
4263 #[test]
4264 fn cluster_delslots_basic() {
4265 assert_eq!(
4266 Command::from_frame(cmd(&["CLUSTER", "DELSLOTS", "100", "101"])).unwrap(),
4267 Command::ClusterDelSlots {
4268 slots: vec![100, 101]
4269 },
4270 );
4271 }
4272
4273 #[test]
4274 fn cluster_forget_basic() {
4275 assert_eq!(
4276 Command::from_frame(cmd(&["CLUSTER", "FORGET", "abc123"])).unwrap(),
4277 Command::ClusterForget {
4278 node_id: "abc123".into()
4279 },
4280 );
4281 }
4282
4283 #[test]
4284 fn cluster_replicate_basic() {
4285 assert_eq!(
4286 Command::from_frame(cmd(&["CLUSTER", "REPLICATE", "master-id"])).unwrap(),
4287 Command::ClusterReplicate {
4288 node_id: "master-id".into()
4289 },
4290 );
4291 }
4292
4293 #[test]
4294 fn cluster_failover_basic() {
4295 assert_eq!(
4296 Command::from_frame(cmd(&["CLUSTER", "FAILOVER"])).unwrap(),
4297 Command::ClusterFailover {
4298 force: false,
4299 takeover: false
4300 },
4301 );
4302 }
4303
4304 #[test]
4305 fn cluster_failover_force() {
4306 assert_eq!(
4307 Command::from_frame(cmd(&["CLUSTER", "FAILOVER", "FORCE"])).unwrap(),
4308 Command::ClusterFailover {
4309 force: true,
4310 takeover: false
4311 },
4312 );
4313 }
4314
4315 #[test]
4316 fn cluster_failover_takeover() {
4317 assert_eq!(
4318 Command::from_frame(cmd(&["CLUSTER", "FAILOVER", "TAKEOVER"])).unwrap(),
4319 Command::ClusterFailover {
4320 force: false,
4321 takeover: true
4322 },
4323 );
4324 }
4325
4326 #[test]
4327 fn cluster_countkeysinslot_basic() {
4328 assert_eq!(
4329 Command::from_frame(cmd(&["CLUSTER", "COUNTKEYSINSLOT", "100"])).unwrap(),
4330 Command::ClusterCountKeysInSlot { slot: 100 },
4331 );
4332 }
4333
4334 #[test]
4335 fn cluster_getkeysinslot_basic() {
4336 assert_eq!(
4337 Command::from_frame(cmd(&["CLUSTER", "GETKEYSINSLOT", "200", "10"])).unwrap(),
4338 Command::ClusterGetKeysInSlot {
4339 slot: 200,
4340 count: 10
4341 },
4342 );
4343 }
4344
4345 #[test]
4348 fn subscribe_single_channel() {
4349 assert_eq!(
4350 Command::from_frame(cmd(&["SUBSCRIBE", "news"])).unwrap(),
4351 Command::Subscribe {
4352 channels: vec!["news".into()]
4353 },
4354 );
4355 }
4356
4357 #[test]
4358 fn subscribe_multiple_channels() {
4359 assert_eq!(
4360 Command::from_frame(cmd(&["SUBSCRIBE", "ch1", "ch2", "ch3"])).unwrap(),
4361 Command::Subscribe {
4362 channels: vec!["ch1".into(), "ch2".into(), "ch3".into()]
4363 },
4364 );
4365 }
4366
4367 #[test]
4368 fn subscribe_no_args() {
4369 let err = Command::from_frame(cmd(&["SUBSCRIBE"])).unwrap_err();
4370 assert!(matches!(err, ProtocolError::WrongArity(_)));
4371 }
4372
4373 #[test]
4374 fn unsubscribe_all() {
4375 assert_eq!(
4376 Command::from_frame(cmd(&["UNSUBSCRIBE"])).unwrap(),
4377 Command::Unsubscribe { channels: vec![] },
4378 );
4379 }
4380
4381 #[test]
4382 fn unsubscribe_specific() {
4383 assert_eq!(
4384 Command::from_frame(cmd(&["UNSUBSCRIBE", "news"])).unwrap(),
4385 Command::Unsubscribe {
4386 channels: vec!["news".into()]
4387 },
4388 );
4389 }
4390
4391 #[test]
4392 fn psubscribe_pattern() {
4393 assert_eq!(
4394 Command::from_frame(cmd(&["PSUBSCRIBE", "news.*"])).unwrap(),
4395 Command::PSubscribe {
4396 patterns: vec!["news.*".into()]
4397 },
4398 );
4399 }
4400
4401 #[test]
4402 fn psubscribe_no_args() {
4403 let err = Command::from_frame(cmd(&["PSUBSCRIBE"])).unwrap_err();
4404 assert!(matches!(err, ProtocolError::WrongArity(_)));
4405 }
4406
4407 #[test]
4408 fn punsubscribe_all() {
4409 assert_eq!(
4410 Command::from_frame(cmd(&["PUNSUBSCRIBE"])).unwrap(),
4411 Command::PUnsubscribe { patterns: vec![] },
4412 );
4413 }
4414
4415 #[test]
4416 fn publish_basic() {
4417 assert_eq!(
4418 Command::from_frame(cmd(&["PUBLISH", "news", "hello world"])).unwrap(),
4419 Command::Publish {
4420 channel: "news".into(),
4421 message: Bytes::from("hello world"),
4422 },
4423 );
4424 }
4425
4426 #[test]
4427 fn publish_wrong_arity() {
4428 let err = Command::from_frame(cmd(&["PUBLISH", "news"])).unwrap_err();
4429 assert!(matches!(err, ProtocolError::WrongArity(_)));
4430 }
4431
4432 #[test]
4433 fn subscribe_case_insensitive() {
4434 assert_eq!(
4435 Command::from_frame(cmd(&["subscribe", "ch"])).unwrap(),
4436 Command::Subscribe {
4437 channels: vec!["ch".into()]
4438 },
4439 );
4440 }
4441
4442 #[test]
4443 fn pubsub_channels_no_pattern() {
4444 assert_eq!(
4445 Command::from_frame(cmd(&["PUBSUB", "CHANNELS"])).unwrap(),
4446 Command::PubSubChannels { pattern: None },
4447 );
4448 }
4449
4450 #[test]
4451 fn pubsub_channels_with_pattern() {
4452 assert_eq!(
4453 Command::from_frame(cmd(&["PUBSUB", "CHANNELS", "news.*"])).unwrap(),
4454 Command::PubSubChannels {
4455 pattern: Some("news.*".into())
4456 },
4457 );
4458 }
4459
4460 #[test]
4461 fn pubsub_numsub_no_args() {
4462 assert_eq!(
4463 Command::from_frame(cmd(&["PUBSUB", "NUMSUB"])).unwrap(),
4464 Command::PubSubNumSub { channels: vec![] },
4465 );
4466 }
4467
4468 #[test]
4469 fn pubsub_numsub_with_channels() {
4470 assert_eq!(
4471 Command::from_frame(cmd(&["PUBSUB", "NUMSUB", "ch1", "ch2"])).unwrap(),
4472 Command::PubSubNumSub {
4473 channels: vec!["ch1".into(), "ch2".into()]
4474 },
4475 );
4476 }
4477
4478 #[test]
4479 fn pubsub_numpat() {
4480 assert_eq!(
4481 Command::from_frame(cmd(&["PUBSUB", "NUMPAT"])).unwrap(),
4482 Command::PubSubNumPat,
4483 );
4484 }
4485
4486 #[test]
4487 fn pubsub_no_subcommand() {
4488 let err = Command::from_frame(cmd(&["PUBSUB"])).unwrap_err();
4489 assert!(matches!(err, ProtocolError::WrongArity(_)));
4490 }
4491
4492 #[test]
4493 fn pubsub_unknown_subcommand() {
4494 let err = Command::from_frame(cmd(&["PUBSUB", "BOGUS"])).unwrap_err();
4495 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
4496 }
4497
4498 #[test]
4501 fn incrby_basic() {
4502 assert_eq!(
4503 Command::from_frame(cmd(&["INCRBY", "counter", "5"])).unwrap(),
4504 Command::IncrBy {
4505 key: "counter".into(),
4506 delta: 5
4507 },
4508 );
4509 }
4510
4511 #[test]
4512 fn incrby_negative() {
4513 assert_eq!(
4514 Command::from_frame(cmd(&["INCRBY", "counter", "-3"])).unwrap(),
4515 Command::IncrBy {
4516 key: "counter".into(),
4517 delta: -3
4518 },
4519 );
4520 }
4521
4522 #[test]
4523 fn incrby_wrong_arity() {
4524 let err = Command::from_frame(cmd(&["INCRBY", "key"])).unwrap_err();
4525 assert!(matches!(err, ProtocolError::WrongArity(_)));
4526 }
4527
4528 #[test]
4529 fn incrby_not_integer() {
4530 let err = Command::from_frame(cmd(&["INCRBY", "key", "abc"])).unwrap_err();
4531 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
4532 }
4533
4534 #[test]
4535 fn decrby_basic() {
4536 assert_eq!(
4537 Command::from_frame(cmd(&["DECRBY", "counter", "10"])).unwrap(),
4538 Command::DecrBy {
4539 key: "counter".into(),
4540 delta: 10
4541 },
4542 );
4543 }
4544
4545 #[test]
4546 fn decrby_wrong_arity() {
4547 let err = Command::from_frame(cmd(&["DECRBY"])).unwrap_err();
4548 assert!(matches!(err, ProtocolError::WrongArity(_)));
4549 }
4550
4551 #[test]
4554 fn incrbyfloat_basic() {
4555 let cmd = Command::from_frame(cmd(&["INCRBYFLOAT", "key", "2.5"])).unwrap();
4556 match cmd {
4557 Command::IncrByFloat { key, delta } => {
4558 assert_eq!(key, "key");
4559 assert!((delta - 2.5).abs() < f64::EPSILON);
4560 }
4561 other => panic!("expected IncrByFloat, got {other:?}"),
4562 }
4563 }
4564
4565 #[test]
4566 fn incrbyfloat_negative() {
4567 let cmd = Command::from_frame(cmd(&["INCRBYFLOAT", "key", "-1.5"])).unwrap();
4568 match cmd {
4569 Command::IncrByFloat { key, delta } => {
4570 assert_eq!(key, "key");
4571 assert!((delta - (-1.5)).abs() < f64::EPSILON);
4572 }
4573 other => panic!("expected IncrByFloat, got {other:?}"),
4574 }
4575 }
4576
4577 #[test]
4578 fn incrbyfloat_wrong_arity() {
4579 let err = Command::from_frame(cmd(&["INCRBYFLOAT", "key"])).unwrap_err();
4580 assert!(matches!(err, ProtocolError::WrongArity(_)));
4581 }
4582
4583 #[test]
4584 fn incrbyfloat_not_a_float() {
4585 let err = Command::from_frame(cmd(&["INCRBYFLOAT", "key", "abc"])).unwrap_err();
4586 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
4587 }
4588
4589 #[test]
4592 fn append_basic() {
4593 assert_eq!(
4594 Command::from_frame(cmd(&["APPEND", "key", "value"])).unwrap(),
4595 Command::Append {
4596 key: "key".into(),
4597 value: Bytes::from("value")
4598 },
4599 );
4600 }
4601
4602 #[test]
4603 fn append_wrong_arity() {
4604 let err = Command::from_frame(cmd(&["APPEND", "key"])).unwrap_err();
4605 assert!(matches!(err, ProtocolError::WrongArity(_)));
4606 }
4607
4608 #[test]
4609 fn strlen_basic() {
4610 assert_eq!(
4611 Command::from_frame(cmd(&["STRLEN", "key"])).unwrap(),
4612 Command::Strlen { key: "key".into() },
4613 );
4614 }
4615
4616 #[test]
4617 fn strlen_wrong_arity() {
4618 let err = Command::from_frame(cmd(&["STRLEN"])).unwrap_err();
4619 assert!(matches!(err, ProtocolError::WrongArity(_)));
4620 }
4621
4622 #[test]
4625 fn keys_basic() {
4626 assert_eq!(
4627 Command::from_frame(cmd(&["KEYS", "user:*"])).unwrap(),
4628 Command::Keys {
4629 pattern: "user:*".into()
4630 },
4631 );
4632 }
4633
4634 #[test]
4635 fn keys_wrong_arity() {
4636 let err = Command::from_frame(cmd(&["KEYS"])).unwrap_err();
4637 assert!(matches!(err, ProtocolError::WrongArity(_)));
4638 }
4639
4640 #[test]
4643 fn rename_basic() {
4644 assert_eq!(
4645 Command::from_frame(cmd(&["RENAME", "old", "new"])).unwrap(),
4646 Command::Rename {
4647 key: "old".into(),
4648 newkey: "new".into()
4649 },
4650 );
4651 }
4652
4653 #[test]
4654 fn rename_wrong_arity() {
4655 let err = Command::from_frame(cmd(&["RENAME", "only"])).unwrap_err();
4656 assert!(matches!(err, ProtocolError::WrongArity(_)));
4657 }
4658
4659 #[test]
4662 fn auth_legacy() {
4663 assert_eq!(
4664 Command::from_frame(cmd(&["AUTH", "secret"])).unwrap(),
4665 Command::Auth {
4666 username: None,
4667 password: "secret".into()
4668 },
4669 );
4670 }
4671
4672 #[test]
4673 fn auth_with_username() {
4674 assert_eq!(
4675 Command::from_frame(cmd(&["AUTH", "default", "secret"])).unwrap(),
4676 Command::Auth {
4677 username: Some("default".into()),
4678 password: "secret".into()
4679 },
4680 );
4681 }
4682
4683 #[test]
4684 fn auth_no_args() {
4685 let err = Command::from_frame(cmd(&["AUTH"])).unwrap_err();
4686 assert!(matches!(err, ProtocolError::WrongArity(_)));
4687 }
4688
4689 #[test]
4690 fn auth_too_many_args() {
4691 let err = Command::from_frame(cmd(&["AUTH", "a", "b", "c"])).unwrap_err();
4692 assert!(matches!(err, ProtocolError::WrongArity(_)));
4693 }
4694
4695 #[test]
4698 fn quit_basic() {
4699 assert_eq!(Command::from_frame(cmd(&["QUIT"])).unwrap(), Command::Quit,);
4700 }
4701
4702 #[test]
4703 fn quit_wrong_arity() {
4704 let err = Command::from_frame(cmd(&["QUIT", "extra"])).unwrap_err();
4705 assert!(matches!(err, ProtocolError::WrongArity(_)));
4706 }
4707
4708 #[test]
4711 fn proto_register_basic() {
4712 assert_eq!(
4713 Command::from_frame(cmd(&["PROTO.REGISTER", "myschema", "descriptor"])).unwrap(),
4714 Command::ProtoRegister {
4715 name: "myschema".into(),
4716 descriptor: Bytes::from("descriptor"),
4717 },
4718 );
4719 }
4720
4721 #[test]
4722 fn proto_register_case_insensitive() {
4723 assert!(Command::from_frame(cmd(&["proto.register", "s", "d"])).is_ok());
4724 }
4725
4726 #[test]
4727 fn proto_register_wrong_arity() {
4728 let err = Command::from_frame(cmd(&["PROTO.REGISTER", "only"])).unwrap_err();
4729 assert!(matches!(err, ProtocolError::WrongArity(_)));
4730 }
4731
4732 #[test]
4735 fn proto_set_basic() {
4736 assert_eq!(
4737 Command::from_frame(cmd(&["PROTO.SET", "key1", "my.Type", "data"])).unwrap(),
4738 Command::ProtoSet {
4739 key: "key1".into(),
4740 type_name: "my.Type".into(),
4741 data: Bytes::from("data"),
4742 expire: None,
4743 nx: false,
4744 xx: false,
4745 },
4746 );
4747 }
4748
4749 #[test]
4750 fn proto_set_with_ex() {
4751 assert_eq!(
4752 Command::from_frame(cmd(&["PROTO.SET", "k", "t", "d", "EX", "60"])).unwrap(),
4753 Command::ProtoSet {
4754 key: "k".into(),
4755 type_name: "t".into(),
4756 data: Bytes::from("d"),
4757 expire: Some(SetExpire::Ex(60)),
4758 nx: false,
4759 xx: false,
4760 },
4761 );
4762 }
4763
4764 #[test]
4765 fn proto_set_with_px_and_nx() {
4766 assert_eq!(
4767 Command::from_frame(cmd(&["PROTO.SET", "k", "t", "d", "PX", "5000", "NX"])).unwrap(),
4768 Command::ProtoSet {
4769 key: "k".into(),
4770 type_name: "t".into(),
4771 data: Bytes::from("d"),
4772 expire: Some(SetExpire::Px(5000)),
4773 nx: true,
4774 xx: false,
4775 },
4776 );
4777 }
4778
4779 #[test]
4780 fn proto_set_nx_xx_conflict() {
4781 let err = Command::from_frame(cmd(&["PROTO.SET", "k", "t", "d", "NX", "XX"])).unwrap_err();
4782 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
4783 }
4784
4785 #[test]
4786 fn proto_set_wrong_arity() {
4787 let err = Command::from_frame(cmd(&["PROTO.SET", "k", "t"])).unwrap_err();
4788 assert!(matches!(err, ProtocolError::WrongArity(_)));
4789 }
4790
4791 #[test]
4792 fn proto_set_zero_expiry() {
4793 let err = Command::from_frame(cmd(&["PROTO.SET", "k", "t", "d", "EX", "0"])).unwrap_err();
4794 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
4795 }
4796
4797 #[test]
4800 fn proto_get_basic() {
4801 assert_eq!(
4802 Command::from_frame(cmd(&["PROTO.GET", "key1"])).unwrap(),
4803 Command::ProtoGet { key: "key1".into() },
4804 );
4805 }
4806
4807 #[test]
4808 fn proto_get_wrong_arity() {
4809 let err = Command::from_frame(cmd(&["PROTO.GET"])).unwrap_err();
4810 assert!(matches!(err, ProtocolError::WrongArity(_)));
4811 }
4812
4813 #[test]
4816 fn proto_type_basic() {
4817 assert_eq!(
4818 Command::from_frame(cmd(&["PROTO.TYPE", "key1"])).unwrap(),
4819 Command::ProtoType { key: "key1".into() },
4820 );
4821 }
4822
4823 #[test]
4824 fn proto_type_wrong_arity() {
4825 let err = Command::from_frame(cmd(&["PROTO.TYPE"])).unwrap_err();
4826 assert!(matches!(err, ProtocolError::WrongArity(_)));
4827 }
4828
4829 #[test]
4832 fn proto_schemas_basic() {
4833 assert_eq!(
4834 Command::from_frame(cmd(&["PROTO.SCHEMAS"])).unwrap(),
4835 Command::ProtoSchemas,
4836 );
4837 }
4838
4839 #[test]
4840 fn proto_schemas_wrong_arity() {
4841 let err = Command::from_frame(cmd(&["PROTO.SCHEMAS", "extra"])).unwrap_err();
4842 assert!(matches!(err, ProtocolError::WrongArity(_)));
4843 }
4844
4845 #[test]
4848 fn proto_describe_basic() {
4849 assert_eq!(
4850 Command::from_frame(cmd(&["PROTO.DESCRIBE", "myschema"])).unwrap(),
4851 Command::ProtoDescribe {
4852 name: "myschema".into()
4853 },
4854 );
4855 }
4856
4857 #[test]
4858 fn proto_describe_wrong_arity() {
4859 let err = Command::from_frame(cmd(&["PROTO.DESCRIBE"])).unwrap_err();
4860 assert!(matches!(err, ProtocolError::WrongArity(_)));
4861 }
4862
4863 #[test]
4866 fn proto_getfield_basic() {
4867 assert_eq!(
4868 Command::from_frame(cmd(&["PROTO.GETFIELD", "user:1", "name"])).unwrap(),
4869 Command::ProtoGetField {
4870 key: "user:1".into(),
4871 field_path: "name".into(),
4872 },
4873 );
4874 }
4875
4876 #[test]
4877 fn proto_getfield_nested_path() {
4878 assert_eq!(
4879 Command::from_frame(cmd(&["PROTO.GETFIELD", "key", "address.city"])).unwrap(),
4880 Command::ProtoGetField {
4881 key: "key".into(),
4882 field_path: "address.city".into(),
4883 },
4884 );
4885 }
4886
4887 #[test]
4888 fn proto_getfield_wrong_arity() {
4889 let err = Command::from_frame(cmd(&["PROTO.GETFIELD"])).unwrap_err();
4890 assert!(matches!(err, ProtocolError::WrongArity(_)));
4891
4892 let err = Command::from_frame(cmd(&["PROTO.GETFIELD", "key"])).unwrap_err();
4893 assert!(matches!(err, ProtocolError::WrongArity(_)));
4894
4895 let err =
4896 Command::from_frame(cmd(&["PROTO.GETFIELD", "key", "field", "extra"])).unwrap_err();
4897 assert!(matches!(err, ProtocolError::WrongArity(_)));
4898 }
4899
4900 #[test]
4903 fn proto_setfield_basic() {
4904 assert_eq!(
4905 Command::from_frame(cmd(&["PROTO.SETFIELD", "user:1", "name", "bob"])).unwrap(),
4906 Command::ProtoSetField {
4907 key: "user:1".into(),
4908 field_path: "name".into(),
4909 value: "bob".into(),
4910 },
4911 );
4912 }
4913
4914 #[test]
4915 fn proto_setfield_wrong_arity() {
4916 let err = Command::from_frame(cmd(&["PROTO.SETFIELD"])).unwrap_err();
4917 assert!(matches!(err, ProtocolError::WrongArity(_)));
4918
4919 let err = Command::from_frame(cmd(&["PROTO.SETFIELD", "key"])).unwrap_err();
4920 assert!(matches!(err, ProtocolError::WrongArity(_)));
4921
4922 let err = Command::from_frame(cmd(&["PROTO.SETFIELD", "key", "field"])).unwrap_err();
4923 assert!(matches!(err, ProtocolError::WrongArity(_)));
4924
4925 let err = Command::from_frame(cmd(&["PROTO.SETFIELD", "key", "field", "value", "extra"]))
4926 .unwrap_err();
4927 assert!(matches!(err, ProtocolError::WrongArity(_)));
4928 }
4929
4930 #[test]
4933 fn proto_delfield_basic() {
4934 assert_eq!(
4935 Command::from_frame(cmd(&["PROTO.DELFIELD", "user:1", "name"])).unwrap(),
4936 Command::ProtoDelField {
4937 key: "user:1".into(),
4938 field_path: "name".into(),
4939 },
4940 );
4941 }
4942
4943 #[test]
4944 fn proto_delfield_wrong_arity() {
4945 let err = Command::from_frame(cmd(&["PROTO.DELFIELD"])).unwrap_err();
4946 assert!(matches!(err, ProtocolError::WrongArity(_)));
4947
4948 let err = Command::from_frame(cmd(&["PROTO.DELFIELD", "key"])).unwrap_err();
4949 assert!(matches!(err, ProtocolError::WrongArity(_)));
4950
4951 let err =
4952 Command::from_frame(cmd(&["PROTO.DELFIELD", "key", "field", "extra"])).unwrap_err();
4953 assert!(matches!(err, ProtocolError::WrongArity(_)));
4954 }
4955
4956 #[test]
4959 fn vadd_basic() {
4960 assert_eq!(
4961 Command::from_frame(cmd(&["VADD", "vecs", "elem1", "0.1", "0.2", "0.3"])).unwrap(),
4962 Command::VAdd {
4963 key: "vecs".into(),
4964 element: "elem1".into(),
4965 vector: vec![0.1, 0.2, 0.3],
4966 metric: 0,
4967 quantization: 0,
4968 connectivity: 16,
4969 expansion_add: 64,
4970 },
4971 );
4972 }
4973
4974 #[test]
4975 fn vadd_with_options() {
4976 assert_eq!(
4977 Command::from_frame(cmd(&[
4978 "VADD", "vecs", "elem1", "1.0", "2.0", "METRIC", "L2", "QUANT", "F16", "M", "32",
4979 "EF", "128"
4980 ]))
4981 .unwrap(),
4982 Command::VAdd {
4983 key: "vecs".into(),
4984 element: "elem1".into(),
4985 vector: vec![1.0, 2.0],
4986 metric: 1,
4987 quantization: 1,
4988 connectivity: 32,
4989 expansion_add: 128,
4990 },
4991 );
4992 }
4993
4994 #[test]
4995 fn vadd_wrong_arity() {
4996 let err = Command::from_frame(cmd(&["VADD"])).unwrap_err();
4998 assert!(matches!(err, ProtocolError::WrongArity(_)));
4999
5000 let err = Command::from_frame(cmd(&["VADD", "key"])).unwrap_err();
5002 assert!(matches!(err, ProtocolError::WrongArity(_)));
5003
5004 let err = Command::from_frame(cmd(&["VADD", "key", "elem"])).unwrap_err();
5006 assert!(matches!(
5007 err,
5008 ProtocolError::WrongArity(_) | ProtocolError::InvalidCommandFrame(_)
5009 ));
5010 }
5011
5012 #[test]
5013 fn vadd_m_exceeds_max() {
5014 let err =
5015 Command::from_frame(cmd(&["VADD", "key", "elem", "1.0", "M", "9999"])).unwrap_err();
5016 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5017 }
5018
5019 #[test]
5020 fn vadd_ef_exceeds_max() {
5021 let err =
5022 Command::from_frame(cmd(&["VADD", "key", "elem", "1.0", "EF", "9999"])).unwrap_err();
5023 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5024 }
5025
5026 #[test]
5027 fn vadd_unknown_metric() {
5028 let err = Command::from_frame(cmd(&["VADD", "key", "elem", "1.0", "METRIC", "HAMMING"]))
5029 .unwrap_err();
5030 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5031 }
5032
5033 #[test]
5034 fn vadd_unknown_quantization() {
5035 let err =
5036 Command::from_frame(cmd(&["VADD", "key", "elem", "1.0", "QUANT", "F64"])).unwrap_err();
5037 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5038 }
5039
5040 #[test]
5043 fn vadd_batch_basic() {
5044 assert_eq!(
5045 Command::from_frame(cmd(&[
5046 "VADD_BATCH",
5047 "vecs",
5048 "DIM",
5049 "3",
5050 "a",
5051 "0.1",
5052 "0.2",
5053 "0.3",
5054 "b",
5055 "0.4",
5056 "0.5",
5057 "0.6"
5058 ]))
5059 .unwrap(),
5060 Command::VAddBatch {
5061 key: "vecs".into(),
5062 entries: vec![
5063 ("a".into(), vec![0.1, 0.2, 0.3]),
5064 ("b".into(), vec![0.4, 0.5, 0.6]),
5065 ],
5066 dim: 3,
5067 metric: 0,
5068 quantization: 0,
5069 connectivity: 16,
5070 expansion_add: 64,
5071 },
5072 );
5073 }
5074
5075 #[test]
5076 fn vadd_batch_with_options() {
5077 assert_eq!(
5078 Command::from_frame(cmd(&[
5079 "VADD_BATCH",
5080 "vecs",
5081 "DIM",
5082 "2",
5083 "a",
5084 "1.0",
5085 "2.0",
5086 "METRIC",
5087 "L2",
5088 "QUANT",
5089 "F16",
5090 "M",
5091 "32",
5092 "EF",
5093 "128"
5094 ]))
5095 .unwrap(),
5096 Command::VAddBatch {
5097 key: "vecs".into(),
5098 entries: vec![("a".into(), vec![1.0, 2.0])],
5099 dim: 2,
5100 metric: 1,
5101 quantization: 1,
5102 connectivity: 32,
5103 expansion_add: 128,
5104 },
5105 );
5106 }
5107
5108 #[test]
5109 fn vadd_batch_single_entry() {
5110 assert_eq!(
5111 Command::from_frame(cmd(&["VADD_BATCH", "vecs", "DIM", "1", "x", "3.14"])).unwrap(),
5112 Command::VAddBatch {
5113 key: "vecs".into(),
5114 entries: vec![("x".into(), vec![3.14])],
5115 dim: 1,
5116 metric: 0,
5117 quantization: 0,
5118 connectivity: 16,
5119 expansion_add: 64,
5120 },
5121 );
5122 }
5123
5124 #[test]
5125 fn vadd_batch_empty_entries() {
5126 assert_eq!(
5128 Command::from_frame(cmd(&["VADD_BATCH", "vecs", "DIM", "3"])).unwrap(),
5129 Command::VAddBatch {
5130 key: "vecs".into(),
5131 entries: vec![],
5132 dim: 3,
5133 metric: 0,
5134 quantization: 0,
5135 connectivity: 16,
5136 expansion_add: 64,
5137 },
5138 );
5139 }
5140
5141 #[test]
5142 fn vadd_batch_wrong_arity() {
5143 let err = Command::from_frame(cmd(&["VADD_BATCH"])).unwrap_err();
5144 assert!(matches!(err, ProtocolError::WrongArity(_)));
5145
5146 let err = Command::from_frame(cmd(&["VADD_BATCH", "key"])).unwrap_err();
5147 assert!(matches!(err, ProtocolError::WrongArity(_)));
5148
5149 let err = Command::from_frame(cmd(&["VADD_BATCH", "key", "DIM"])).unwrap_err();
5150 assert!(matches!(err, ProtocolError::WrongArity(_)));
5151 }
5152
5153 #[test]
5154 fn vadd_batch_missing_dim_keyword() {
5155 let err = Command::from_frame(cmd(&["VADD_BATCH", "key", "3", "a", "1.0"])).unwrap_err();
5156 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5157 }
5158
5159 #[test]
5160 fn vadd_batch_dim_zero() {
5161 let err = Command::from_frame(cmd(&["VADD_BATCH", "key", "DIM", "0"])).unwrap_err();
5162 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5163 }
5164
5165 #[test]
5166 fn vadd_batch_dim_exceeds_max() {
5167 let err = Command::from_frame(cmd(&["VADD_BATCH", "key", "DIM", "99999"])).unwrap_err();
5168 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5169 }
5170
5171 #[test]
5172 fn vadd_batch_insufficient_floats() {
5173 let err = Command::from_frame(cmd(&["VADD_BATCH", "key", "DIM", "3", "a", "1.0", "2.0"]))
5175 .unwrap_err();
5176 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5177 }
5178
5179 #[test]
5180 fn vadd_batch_m_exceeds_max() {
5181 let err = Command::from_frame(cmd(&[
5182 "VADD_BATCH",
5183 "key",
5184 "DIM",
5185 "2",
5186 "a",
5187 "1.0",
5188 "2.0",
5189 "M",
5190 "9999",
5191 ]))
5192 .unwrap_err();
5193 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5194 }
5195
5196 #[test]
5197 fn vadd_batch_ef_exceeds_max() {
5198 let err = Command::from_frame(cmd(&[
5199 "VADD_BATCH",
5200 "key",
5201 "DIM",
5202 "2",
5203 "a",
5204 "1.0",
5205 "2.0",
5206 "EF",
5207 "9999",
5208 ]))
5209 .unwrap_err();
5210 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5211 }
5212
5213 #[test]
5214 fn vsim_basic() {
5215 assert_eq!(
5216 Command::from_frame(cmd(&["VSIM", "vecs", "0.1", "0.2", "0.3", "COUNT", "5"])).unwrap(),
5217 Command::VSim {
5218 key: "vecs".into(),
5219 query: vec![0.1, 0.2, 0.3],
5220 count: 5,
5221 ef_search: 0,
5222 with_scores: false,
5223 },
5224 );
5225 }
5226
5227 #[test]
5228 fn vsim_with_ef_and_scores() {
5229 assert_eq!(
5230 Command::from_frame(cmd(&[
5231 "VSIM",
5232 "vecs",
5233 "1.0",
5234 "2.0",
5235 "COUNT",
5236 "10",
5237 "EF",
5238 "128",
5239 "WITHSCORES"
5240 ]))
5241 .unwrap(),
5242 Command::VSim {
5243 key: "vecs".into(),
5244 query: vec![1.0, 2.0],
5245 count: 10,
5246 ef_search: 128,
5247 with_scores: true,
5248 },
5249 );
5250 }
5251
5252 #[test]
5253 fn vsim_missing_count() {
5254 let err = Command::from_frame(cmd(&["VSIM", "key", "1.0", "2.0"])).unwrap_err();
5256 assert!(matches!(
5257 err,
5258 ProtocolError::InvalidCommandFrame(_) | ProtocolError::WrongArity(_)
5259 ));
5260 }
5261
5262 #[test]
5263 fn vsim_wrong_arity() {
5264 let err = Command::from_frame(cmd(&["VSIM"])).unwrap_err();
5265 assert!(matches!(err, ProtocolError::WrongArity(_)));
5266 }
5267
5268 #[test]
5269 fn vsim_count_exceeds_max() {
5270 let err = Command::from_frame(cmd(&["VSIM", "key", "1.0", "COUNT", "99999"])).unwrap_err();
5271 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5272 }
5273
5274 #[test]
5275 fn vsim_ef_exceeds_max() {
5276 let err = Command::from_frame(cmd(&["VSIM", "key", "1.0", "COUNT", "5", "EF", "9999"]))
5277 .unwrap_err();
5278 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
5279 }
5280
5281 #[test]
5282 fn vrem_basic() {
5283 assert_eq!(
5284 Command::from_frame(cmd(&["VREM", "key", "elem"])).unwrap(),
5285 Command::VRem {
5286 key: "key".into(),
5287 element: "elem".into(),
5288 },
5289 );
5290 }
5291
5292 #[test]
5293 fn vrem_wrong_arity() {
5294 let err = Command::from_frame(cmd(&["VREM"])).unwrap_err();
5295 assert!(matches!(err, ProtocolError::WrongArity(_)));
5296
5297 let err = Command::from_frame(cmd(&["VREM", "key"])).unwrap_err();
5298 assert!(matches!(err, ProtocolError::WrongArity(_)));
5299 }
5300
5301 #[test]
5302 fn vget_basic() {
5303 assert_eq!(
5304 Command::from_frame(cmd(&["VGET", "key", "elem"])).unwrap(),
5305 Command::VGet {
5306 key: "key".into(),
5307 element: "elem".into(),
5308 },
5309 );
5310 }
5311
5312 #[test]
5313 fn vget_wrong_arity() {
5314 let err = Command::from_frame(cmd(&["VGET"])).unwrap_err();
5315 assert!(matches!(err, ProtocolError::WrongArity(_)));
5316 }
5317
5318 #[test]
5319 fn vcard_basic() {
5320 assert_eq!(
5321 Command::from_frame(cmd(&["VCARD", "key"])).unwrap(),
5322 Command::VCard { key: "key".into() },
5323 );
5324 }
5325
5326 #[test]
5327 fn vcard_wrong_arity() {
5328 let err = Command::from_frame(cmd(&["VCARD"])).unwrap_err();
5329 assert!(matches!(err, ProtocolError::WrongArity(_)));
5330 }
5331
5332 #[test]
5333 fn vdim_basic() {
5334 assert_eq!(
5335 Command::from_frame(cmd(&["VDIM", "key"])).unwrap(),
5336 Command::VDim { key: "key".into() },
5337 );
5338 }
5339
5340 #[test]
5341 fn vdim_wrong_arity() {
5342 let err = Command::from_frame(cmd(&["VDIM"])).unwrap_err();
5343 assert!(matches!(err, ProtocolError::WrongArity(_)));
5344 }
5345
5346 #[test]
5347 fn vinfo_basic() {
5348 assert_eq!(
5349 Command::from_frame(cmd(&["VINFO", "key"])).unwrap(),
5350 Command::VInfo { key: "key".into() },
5351 );
5352 }
5353
5354 #[test]
5355 fn vinfo_wrong_arity() {
5356 let err = Command::from_frame(cmd(&["VINFO"])).unwrap_err();
5357 assert!(matches!(err, ProtocolError::WrongArity(_)));
5358 }
5359}