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