1use bytes::Bytes;
8
9use crate::error::ProtocolError;
10use crate::types::Frame;
11
12#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum SetExpire {
15 Ex(u64),
17 Px(u64),
19}
20
21#[derive(Debug, Clone, PartialEq)]
23pub enum Command {
24 Ping(Option<Bytes>),
26
27 Echo(Bytes),
29
30 Get { key: String },
32
33 Set {
35 key: String,
36 value: Bytes,
37 expire: Option<SetExpire>,
38 nx: bool,
40 xx: bool,
42 },
43
44 Incr { key: String },
46
47 Decr { key: String },
49
50 Del { keys: Vec<String> },
52
53 Exists { keys: Vec<String> },
55
56 MGet { keys: Vec<String> },
58
59 MSet { pairs: Vec<(String, Bytes)> },
61
62 Expire { key: String, seconds: u64 },
64
65 Ttl { key: String },
67
68 Persist { key: String },
70
71 Pttl { key: String },
73
74 Pexpire { key: String, milliseconds: u64 },
76
77 DbSize,
79
80 Info { section: Option<String> },
82
83 BgSave,
85
86 BgRewriteAof,
88
89 FlushDb,
91
92 Scan {
94 cursor: u64,
95 pattern: Option<String>,
96 count: Option<usize>,
97 },
98
99 LPush { key: String, values: Vec<Bytes> },
101
102 RPush { key: String, values: Vec<Bytes> },
104
105 LPop { key: String },
107
108 RPop { key: String },
110
111 LRange { key: String, start: i64, stop: i64 },
113
114 LLen { key: String },
116
117 Type { key: String },
119
120 ZAdd {
122 key: String,
123 flags: ZAddFlags,
124 members: Vec<(f64, String)>,
125 },
126
127 ZRem { key: String, members: Vec<String> },
129
130 ZScore { key: String, member: String },
132
133 ZRank { key: String, member: String },
135
136 ZCard { key: String },
138
139 ZRange {
141 key: String,
142 start: i64,
143 stop: i64,
144 with_scores: bool,
145 },
146
147 HSet {
149 key: String,
150 fields: Vec<(String, Bytes)>,
151 },
152
153 HGet { key: String, field: String },
155
156 HGetAll { key: String },
158
159 HDel { key: String, fields: Vec<String> },
161
162 HExists { key: String, field: String },
164
165 HLen { key: String },
167
168 HIncrBy {
170 key: String,
171 field: String,
172 delta: i64,
173 },
174
175 HKeys { key: String },
177
178 HVals { key: String },
180
181 HMGet { key: String, fields: Vec<String> },
183
184 SAdd { key: String, members: Vec<String> },
186
187 SRem { key: String, members: Vec<String> },
189
190 SMembers { key: String },
192
193 SIsMember { key: String, member: String },
195
196 SCard { key: String },
198
199 Unknown(String),
201}
202
203#[derive(Debug, Clone, Default, PartialEq)]
205pub struct ZAddFlags {
206 pub nx: bool,
208 pub xx: bool,
210 pub gt: bool,
212 pub lt: bool,
214 pub ch: bool,
216}
217
218impl Eq for ZAddFlags {}
219
220impl Command {
221 pub fn from_frame(frame: Frame) -> Result<Command, ProtocolError> {
226 let frames = match frame {
227 Frame::Array(frames) => frames,
228 _ => {
229 return Err(ProtocolError::InvalidCommandFrame(
230 "expected array frame".into(),
231 ));
232 }
233 };
234
235 if frames.is_empty() {
236 return Err(ProtocolError::InvalidCommandFrame(
237 "empty command array".into(),
238 ));
239 }
240
241 let name = extract_string(&frames[0])?;
242 let name_upper = name.to_ascii_uppercase();
243
244 match name_upper.as_str() {
245 "PING" => parse_ping(&frames[1..]),
246 "ECHO" => parse_echo(&frames[1..]),
247 "GET" => parse_get(&frames[1..]),
248 "SET" => parse_set(&frames[1..]),
249 "INCR" => parse_incr(&frames[1..]),
250 "DECR" => parse_decr(&frames[1..]),
251 "DEL" => parse_del(&frames[1..]),
252 "EXISTS" => parse_exists(&frames[1..]),
253 "MGET" => parse_mget(&frames[1..]),
254 "MSET" => parse_mset(&frames[1..]),
255 "EXPIRE" => parse_expire(&frames[1..]),
256 "TTL" => parse_ttl(&frames[1..]),
257 "PERSIST" => parse_persist(&frames[1..]),
258 "PTTL" => parse_pttl(&frames[1..]),
259 "PEXPIRE" => parse_pexpire(&frames[1..]),
260 "DBSIZE" => parse_dbsize(&frames[1..]),
261 "INFO" => parse_info(&frames[1..]),
262 "BGSAVE" => parse_bgsave(&frames[1..]),
263 "BGREWRITEAOF" => parse_bgrewriteaof(&frames[1..]),
264 "FLUSHDB" => parse_flushdb(&frames[1..]),
265 "SCAN" => parse_scan(&frames[1..]),
266 "LPUSH" => parse_lpush(&frames[1..]),
267 "RPUSH" => parse_rpush(&frames[1..]),
268 "LPOP" => parse_lpop(&frames[1..]),
269 "RPOP" => parse_rpop(&frames[1..]),
270 "LRANGE" => parse_lrange(&frames[1..]),
271 "LLEN" => parse_llen(&frames[1..]),
272 "TYPE" => parse_type(&frames[1..]),
273 "ZADD" => parse_zadd(&frames[1..]),
274 "ZREM" => parse_zrem(&frames[1..]),
275 "ZSCORE" => parse_zscore(&frames[1..]),
276 "ZRANK" => parse_zrank(&frames[1..]),
277 "ZCARD" => parse_zcard(&frames[1..]),
278 "ZRANGE" => parse_zrange(&frames[1..]),
279 "HSET" => parse_hset(&frames[1..]),
280 "HGET" => parse_hget(&frames[1..]),
281 "HGETALL" => parse_hgetall(&frames[1..]),
282 "HDEL" => parse_hdel(&frames[1..]),
283 "HEXISTS" => parse_hexists(&frames[1..]),
284 "HLEN" => parse_hlen(&frames[1..]),
285 "HINCRBY" => parse_hincrby(&frames[1..]),
286 "HKEYS" => parse_hkeys(&frames[1..]),
287 "HVALS" => parse_hvals(&frames[1..]),
288 "HMGET" => parse_hmget(&frames[1..]),
289 "SADD" => parse_sadd(&frames[1..]),
290 "SREM" => parse_srem(&frames[1..]),
291 "SMEMBERS" => parse_smembers(&frames[1..]),
292 "SISMEMBER" => parse_sismember(&frames[1..]),
293 "SCARD" => parse_scard(&frames[1..]),
294 _ => Ok(Command::Unknown(name)),
295 }
296 }
297}
298
299fn extract_string(frame: &Frame) -> Result<String, ProtocolError> {
301 match frame {
302 Frame::Bulk(data) => String::from_utf8(data.to_vec()).map_err(|_| {
303 ProtocolError::InvalidCommandFrame("command name is not valid utf-8".into())
304 }),
305 Frame::Simple(s) => Ok(s.clone()),
306 _ => Err(ProtocolError::InvalidCommandFrame(
307 "expected bulk or simple string for command name".into(),
308 )),
309 }
310}
311
312fn extract_bytes(frame: &Frame) -> Result<Bytes, ProtocolError> {
314 match frame {
315 Frame::Bulk(data) => Ok(data.clone()),
316 Frame::Simple(s) => Ok(Bytes::from(s.clone().into_bytes())),
317 _ => Err(ProtocolError::InvalidCommandFrame(
318 "expected bulk or simple string argument".into(),
319 )),
320 }
321}
322
323fn parse_u64(frame: &Frame, cmd: &str) -> Result<u64, ProtocolError> {
325 let s = extract_string(frame)?;
326 s.parse::<u64>().map_err(|_| {
327 ProtocolError::InvalidCommandFrame(format!("value is not a valid integer for '{cmd}'"))
328 })
329}
330
331fn parse_ping(args: &[Frame]) -> Result<Command, ProtocolError> {
332 match args.len() {
333 0 => Ok(Command::Ping(None)),
334 1 => {
335 let msg = extract_bytes(&args[0])?;
336 Ok(Command::Ping(Some(msg)))
337 }
338 _ => Err(ProtocolError::WrongArity("PING".into())),
339 }
340}
341
342fn parse_echo(args: &[Frame]) -> Result<Command, ProtocolError> {
343 if args.len() != 1 {
344 return Err(ProtocolError::WrongArity("ECHO".into()));
345 }
346 let msg = extract_bytes(&args[0])?;
347 Ok(Command::Echo(msg))
348}
349
350fn parse_get(args: &[Frame]) -> Result<Command, ProtocolError> {
351 if args.len() != 1 {
352 return Err(ProtocolError::WrongArity("GET".into()));
353 }
354 let key = extract_string(&args[0])?;
355 Ok(Command::Get { key })
356}
357
358fn parse_set(args: &[Frame]) -> Result<Command, ProtocolError> {
359 if args.len() < 2 {
360 return Err(ProtocolError::WrongArity("SET".into()));
361 }
362
363 let key = extract_string(&args[0])?;
364 let value = extract_bytes(&args[1])?;
365
366 let mut expire = None;
367 let mut nx = false;
368 let mut xx = false;
369 let mut idx = 2;
370
371 while idx < args.len() {
372 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
373 match flag.as_str() {
374 "NX" => {
375 nx = true;
376 idx += 1;
377 }
378 "XX" => {
379 xx = true;
380 idx += 1;
381 }
382 "EX" => {
383 idx += 1;
384 if idx >= args.len() {
385 return Err(ProtocolError::WrongArity("SET".into()));
386 }
387 let amount = parse_u64(&args[idx], "SET")?;
388 if amount == 0 {
389 return Err(ProtocolError::InvalidCommandFrame(
390 "invalid expire time in 'SET' command".into(),
391 ));
392 }
393 expire = Some(SetExpire::Ex(amount));
394 idx += 1;
395 }
396 "PX" => {
397 idx += 1;
398 if idx >= args.len() {
399 return Err(ProtocolError::WrongArity("SET".into()));
400 }
401 let amount = parse_u64(&args[idx], "SET")?;
402 if amount == 0 {
403 return Err(ProtocolError::InvalidCommandFrame(
404 "invalid expire time in 'SET' command".into(),
405 ));
406 }
407 expire = Some(SetExpire::Px(amount));
408 idx += 1;
409 }
410 _ => {
411 return Err(ProtocolError::InvalidCommandFrame(format!(
412 "unsupported SET option '{flag}'"
413 )));
414 }
415 }
416 }
417
418 if nx && xx {
419 return Err(ProtocolError::InvalidCommandFrame(
420 "XX and NX options at the same time are not compatible".into(),
421 ));
422 }
423
424 Ok(Command::Set {
425 key,
426 value,
427 expire,
428 nx,
429 xx,
430 })
431}
432
433fn parse_incr(args: &[Frame]) -> Result<Command, ProtocolError> {
434 if args.len() != 1 {
435 return Err(ProtocolError::WrongArity("INCR".into()));
436 }
437 let key = extract_string(&args[0])?;
438 Ok(Command::Incr { key })
439}
440
441fn parse_decr(args: &[Frame]) -> Result<Command, ProtocolError> {
442 if args.len() != 1 {
443 return Err(ProtocolError::WrongArity("DECR".into()));
444 }
445 let key = extract_string(&args[0])?;
446 Ok(Command::Decr { key })
447}
448
449fn parse_del(args: &[Frame]) -> Result<Command, ProtocolError> {
450 if args.is_empty() {
451 return Err(ProtocolError::WrongArity("DEL".into()));
452 }
453 let keys = args
454 .iter()
455 .map(extract_string)
456 .collect::<Result<Vec<_>, _>>()?;
457 Ok(Command::Del { keys })
458}
459
460fn parse_exists(args: &[Frame]) -> Result<Command, ProtocolError> {
461 if args.is_empty() {
462 return Err(ProtocolError::WrongArity("EXISTS".into()));
463 }
464 let keys = args
465 .iter()
466 .map(extract_string)
467 .collect::<Result<Vec<_>, _>>()?;
468 Ok(Command::Exists { keys })
469}
470
471fn parse_mget(args: &[Frame]) -> Result<Command, ProtocolError> {
472 if args.is_empty() {
473 return Err(ProtocolError::WrongArity("MGET".into()));
474 }
475 let keys = args
476 .iter()
477 .map(extract_string)
478 .collect::<Result<Vec<_>, _>>()?;
479 Ok(Command::MGet { keys })
480}
481
482fn parse_mset(args: &[Frame]) -> Result<Command, ProtocolError> {
483 if args.is_empty() || !args.len().is_multiple_of(2) {
484 return Err(ProtocolError::WrongArity("MSET".into()));
485 }
486 let mut pairs = Vec::with_capacity(args.len() / 2);
487 for chunk in args.chunks(2) {
488 let key = extract_string(&chunk[0])?;
489 let value = extract_bytes(&chunk[1])?;
490 pairs.push((key, value));
491 }
492 Ok(Command::MSet { pairs })
493}
494
495fn parse_expire(args: &[Frame]) -> Result<Command, ProtocolError> {
496 if args.len() != 2 {
497 return Err(ProtocolError::WrongArity("EXPIRE".into()));
498 }
499 let key = extract_string(&args[0])?;
500 let seconds = parse_u64(&args[1], "EXPIRE")?;
501
502 if seconds == 0 {
503 return Err(ProtocolError::InvalidCommandFrame(
504 "invalid expire time in 'EXPIRE' command".into(),
505 ));
506 }
507
508 Ok(Command::Expire { key, seconds })
509}
510
511fn parse_ttl(args: &[Frame]) -> Result<Command, ProtocolError> {
512 if args.len() != 1 {
513 return Err(ProtocolError::WrongArity("TTL".into()));
514 }
515 let key = extract_string(&args[0])?;
516 Ok(Command::Ttl { key })
517}
518
519fn parse_persist(args: &[Frame]) -> Result<Command, ProtocolError> {
520 if args.len() != 1 {
521 return Err(ProtocolError::WrongArity("PERSIST".into()));
522 }
523 let key = extract_string(&args[0])?;
524 Ok(Command::Persist { key })
525}
526
527fn parse_pttl(args: &[Frame]) -> Result<Command, ProtocolError> {
528 if args.len() != 1 {
529 return Err(ProtocolError::WrongArity("PTTL".into()));
530 }
531 let key = extract_string(&args[0])?;
532 Ok(Command::Pttl { key })
533}
534
535fn parse_pexpire(args: &[Frame]) -> Result<Command, ProtocolError> {
536 if args.len() != 2 {
537 return Err(ProtocolError::WrongArity("PEXPIRE".into()));
538 }
539 let key = extract_string(&args[0])?;
540 let milliseconds = parse_u64(&args[1], "PEXPIRE")?;
541
542 if milliseconds == 0 {
543 return Err(ProtocolError::InvalidCommandFrame(
544 "invalid expire time in 'PEXPIRE' command".into(),
545 ));
546 }
547
548 Ok(Command::Pexpire { key, milliseconds })
549}
550
551fn parse_dbsize(args: &[Frame]) -> Result<Command, ProtocolError> {
552 if !args.is_empty() {
553 return Err(ProtocolError::WrongArity("DBSIZE".into()));
554 }
555 Ok(Command::DbSize)
556}
557
558fn parse_info(args: &[Frame]) -> Result<Command, ProtocolError> {
559 match args.len() {
560 0 => Ok(Command::Info { section: None }),
561 1 => {
562 let section = extract_string(&args[0])?;
563 Ok(Command::Info {
564 section: Some(section),
565 })
566 }
567 _ => Err(ProtocolError::WrongArity("INFO".into())),
568 }
569}
570
571fn parse_bgsave(args: &[Frame]) -> Result<Command, ProtocolError> {
572 if !args.is_empty() {
573 return Err(ProtocolError::WrongArity("BGSAVE".into()));
574 }
575 Ok(Command::BgSave)
576}
577
578fn parse_bgrewriteaof(args: &[Frame]) -> Result<Command, ProtocolError> {
579 if !args.is_empty() {
580 return Err(ProtocolError::WrongArity("BGREWRITEAOF".into()));
581 }
582 Ok(Command::BgRewriteAof)
583}
584
585fn parse_flushdb(args: &[Frame]) -> Result<Command, ProtocolError> {
586 if !args.is_empty() {
587 return Err(ProtocolError::WrongArity("FLUSHDB".into()));
588 }
589 Ok(Command::FlushDb)
590}
591
592fn parse_scan(args: &[Frame]) -> Result<Command, ProtocolError> {
593 if args.is_empty() {
594 return Err(ProtocolError::WrongArity("SCAN".into()));
595 }
596
597 let cursor = parse_u64(&args[0], "SCAN")?;
598 let mut pattern = None;
599 let mut count = None;
600 let mut idx = 1;
601
602 while idx < args.len() {
603 let flag = extract_string(&args[idx])?.to_ascii_uppercase();
604 match flag.as_str() {
605 "MATCH" => {
606 idx += 1;
607 if idx >= args.len() {
608 return Err(ProtocolError::WrongArity("SCAN".into()));
609 }
610 pattern = Some(extract_string(&args[idx])?);
611 idx += 1;
612 }
613 "COUNT" => {
614 idx += 1;
615 if idx >= args.len() {
616 return Err(ProtocolError::WrongArity("SCAN".into()));
617 }
618 let n = parse_u64(&args[idx], "SCAN")?;
619 count = Some(n as usize);
620 idx += 1;
621 }
622 _ => {
623 return Err(ProtocolError::InvalidCommandFrame(format!(
624 "unsupported SCAN option '{flag}'"
625 )));
626 }
627 }
628 }
629
630 Ok(Command::Scan {
631 cursor,
632 pattern,
633 count,
634 })
635}
636
637fn parse_i64(frame: &Frame, cmd: &str) -> Result<i64, ProtocolError> {
639 let s = extract_string(frame)?;
640 s.parse::<i64>().map_err(|_| {
641 ProtocolError::InvalidCommandFrame(format!("value is not a valid integer for '{cmd}'"))
642 })
643}
644
645fn parse_lpush(args: &[Frame]) -> Result<Command, ProtocolError> {
646 if args.len() < 2 {
647 return Err(ProtocolError::WrongArity("LPUSH".into()));
648 }
649 let key = extract_string(&args[0])?;
650 let values = args[1..]
651 .iter()
652 .map(extract_bytes)
653 .collect::<Result<Vec<_>, _>>()?;
654 Ok(Command::LPush { key, values })
655}
656
657fn parse_rpush(args: &[Frame]) -> Result<Command, ProtocolError> {
658 if args.len() < 2 {
659 return Err(ProtocolError::WrongArity("RPUSH".into()));
660 }
661 let key = extract_string(&args[0])?;
662 let values = args[1..]
663 .iter()
664 .map(extract_bytes)
665 .collect::<Result<Vec<_>, _>>()?;
666 Ok(Command::RPush { key, values })
667}
668
669fn parse_lpop(args: &[Frame]) -> Result<Command, ProtocolError> {
670 if args.len() != 1 {
671 return Err(ProtocolError::WrongArity("LPOP".into()));
672 }
673 let key = extract_string(&args[0])?;
674 Ok(Command::LPop { key })
675}
676
677fn parse_rpop(args: &[Frame]) -> Result<Command, ProtocolError> {
678 if args.len() != 1 {
679 return Err(ProtocolError::WrongArity("RPOP".into()));
680 }
681 let key = extract_string(&args[0])?;
682 Ok(Command::RPop { key })
683}
684
685fn parse_lrange(args: &[Frame]) -> Result<Command, ProtocolError> {
686 if args.len() != 3 {
687 return Err(ProtocolError::WrongArity("LRANGE".into()));
688 }
689 let key = extract_string(&args[0])?;
690 let start = parse_i64(&args[1], "LRANGE")?;
691 let stop = parse_i64(&args[2], "LRANGE")?;
692 Ok(Command::LRange { key, start, stop })
693}
694
695fn parse_llen(args: &[Frame]) -> Result<Command, ProtocolError> {
696 if args.len() != 1 {
697 return Err(ProtocolError::WrongArity("LLEN".into()));
698 }
699 let key = extract_string(&args[0])?;
700 Ok(Command::LLen { key })
701}
702
703fn parse_type(args: &[Frame]) -> Result<Command, ProtocolError> {
704 if args.len() != 1 {
705 return Err(ProtocolError::WrongArity("TYPE".into()));
706 }
707 let key = extract_string(&args[0])?;
708 Ok(Command::Type { key })
709}
710
711fn parse_f64(frame: &Frame, cmd: &str) -> Result<f64, ProtocolError> {
713 let s = extract_string(frame)?;
714 let v = s.parse::<f64>().map_err(|_| {
715 ProtocolError::InvalidCommandFrame(format!("value is not a valid float for '{cmd}'"))
716 })?;
717 if v.is_nan() {
718 return Err(ProtocolError::InvalidCommandFrame(format!(
719 "NaN is not a valid score for '{cmd}'"
720 )));
721 }
722 Ok(v)
723}
724
725fn parse_zadd(args: &[Frame]) -> Result<Command, ProtocolError> {
726 if args.len() < 3 {
728 return Err(ProtocolError::WrongArity("ZADD".into()));
729 }
730
731 let key = extract_string(&args[0])?;
732 let mut flags = ZAddFlags::default();
733 let mut idx = 1;
734
735 while idx < args.len() {
737 let s = extract_string(&args[idx])?.to_ascii_uppercase();
738 match s.as_str() {
739 "NX" => {
740 flags.nx = true;
741 idx += 1;
742 }
743 "XX" => {
744 flags.xx = true;
745 idx += 1;
746 }
747 "GT" => {
748 flags.gt = true;
749 idx += 1;
750 }
751 "LT" => {
752 flags.lt = true;
753 idx += 1;
754 }
755 "CH" => {
756 flags.ch = true;
757 idx += 1;
758 }
759 _ => break,
760 }
761 }
762
763 if flags.nx && flags.xx {
765 return Err(ProtocolError::InvalidCommandFrame(
766 "XX and NX options at the same time are not compatible".into(),
767 ));
768 }
769 if flags.gt && flags.lt {
771 return Err(ProtocolError::InvalidCommandFrame(
772 "GT and LT options at the same time are not compatible".into(),
773 ));
774 }
775
776 let remaining = &args[idx..];
778 if remaining.is_empty() || !remaining.len().is_multiple_of(2) {
779 return Err(ProtocolError::WrongArity("ZADD".into()));
780 }
781
782 let mut members = Vec::with_capacity(remaining.len() / 2);
783 for pair in remaining.chunks(2) {
784 let score = parse_f64(&pair[0], "ZADD")?;
785 let member = extract_string(&pair[1])?;
786 members.push((score, member));
787 }
788
789 Ok(Command::ZAdd {
790 key,
791 flags,
792 members,
793 })
794}
795
796fn parse_zcard(args: &[Frame]) -> Result<Command, ProtocolError> {
797 if args.len() != 1 {
798 return Err(ProtocolError::WrongArity("ZCARD".into()));
799 }
800 let key = extract_string(&args[0])?;
801 Ok(Command::ZCard { key })
802}
803
804fn parse_zrem(args: &[Frame]) -> Result<Command, ProtocolError> {
805 if args.len() < 2 {
806 return Err(ProtocolError::WrongArity("ZREM".into()));
807 }
808 let key = extract_string(&args[0])?;
809 let members = args[1..]
810 .iter()
811 .map(extract_string)
812 .collect::<Result<Vec<_>, _>>()?;
813 Ok(Command::ZRem { key, members })
814}
815
816fn parse_zscore(args: &[Frame]) -> Result<Command, ProtocolError> {
817 if args.len() != 2 {
818 return Err(ProtocolError::WrongArity("ZSCORE".into()));
819 }
820 let key = extract_string(&args[0])?;
821 let member = extract_string(&args[1])?;
822 Ok(Command::ZScore { key, member })
823}
824
825fn parse_zrank(args: &[Frame]) -> Result<Command, ProtocolError> {
826 if args.len() != 2 {
827 return Err(ProtocolError::WrongArity("ZRANK".into()));
828 }
829 let key = extract_string(&args[0])?;
830 let member = extract_string(&args[1])?;
831 Ok(Command::ZRank { key, member })
832}
833
834fn parse_zrange(args: &[Frame]) -> Result<Command, ProtocolError> {
835 if args.len() < 3 || args.len() > 4 {
836 return Err(ProtocolError::WrongArity("ZRANGE".into()));
837 }
838 let key = extract_string(&args[0])?;
839 let start = parse_i64(&args[1], "ZRANGE")?;
840 let stop = parse_i64(&args[2], "ZRANGE")?;
841
842 let with_scores = if args.len() == 4 {
843 let opt = extract_string(&args[3])?.to_ascii_uppercase();
844 if opt != "WITHSCORES" {
845 return Err(ProtocolError::InvalidCommandFrame(format!(
846 "unsupported ZRANGE option '{opt}'"
847 )));
848 }
849 true
850 } else {
851 false
852 };
853
854 Ok(Command::ZRange {
855 key,
856 start,
857 stop,
858 with_scores,
859 })
860}
861
862fn parse_hset(args: &[Frame]) -> Result<Command, ProtocolError> {
865 if args.len() < 3 || !(args.len() - 1).is_multiple_of(2) {
869 return Err(ProtocolError::WrongArity("HSET".into()));
870 }
871
872 let key = extract_string(&args[0])?;
873 let mut fields = Vec::with_capacity((args.len() - 1) / 2);
874
875 for chunk in args[1..].chunks(2) {
876 let field = extract_string(&chunk[0])?;
877 let value = extract_bytes(&chunk[1])?;
878 fields.push((field, value));
879 }
880
881 Ok(Command::HSet { key, fields })
882}
883
884fn parse_hget(args: &[Frame]) -> Result<Command, ProtocolError> {
885 if args.len() != 2 {
886 return Err(ProtocolError::WrongArity("HGET".into()));
887 }
888 let key = extract_string(&args[0])?;
889 let field = extract_string(&args[1])?;
890 Ok(Command::HGet { key, field })
891}
892
893fn parse_hgetall(args: &[Frame]) -> Result<Command, ProtocolError> {
894 if args.len() != 1 {
895 return Err(ProtocolError::WrongArity("HGETALL".into()));
896 }
897 let key = extract_string(&args[0])?;
898 Ok(Command::HGetAll { key })
899}
900
901fn parse_hdel(args: &[Frame]) -> Result<Command, ProtocolError> {
902 if args.len() < 2 {
903 return Err(ProtocolError::WrongArity("HDEL".into()));
904 }
905 let key = extract_string(&args[0])?;
906 let fields = args[1..]
907 .iter()
908 .map(extract_string)
909 .collect::<Result<Vec<_>, _>>()?;
910 Ok(Command::HDel { key, fields })
911}
912
913fn parse_hexists(args: &[Frame]) -> Result<Command, ProtocolError> {
914 if args.len() != 2 {
915 return Err(ProtocolError::WrongArity("HEXISTS".into()));
916 }
917 let key = extract_string(&args[0])?;
918 let field = extract_string(&args[1])?;
919 Ok(Command::HExists { key, field })
920}
921
922fn parse_hlen(args: &[Frame]) -> Result<Command, ProtocolError> {
923 if args.len() != 1 {
924 return Err(ProtocolError::WrongArity("HLEN".into()));
925 }
926 let key = extract_string(&args[0])?;
927 Ok(Command::HLen { key })
928}
929
930fn parse_hincrby(args: &[Frame]) -> Result<Command, ProtocolError> {
931 if args.len() != 3 {
932 return Err(ProtocolError::WrongArity("HINCRBY".into()));
933 }
934 let key = extract_string(&args[0])?;
935 let field = extract_string(&args[1])?;
936 let delta = parse_i64(&args[2], "HINCRBY")?;
937 Ok(Command::HIncrBy { key, field, delta })
938}
939
940fn parse_hkeys(args: &[Frame]) -> Result<Command, ProtocolError> {
941 if args.len() != 1 {
942 return Err(ProtocolError::WrongArity("HKEYS".into()));
943 }
944 let key = extract_string(&args[0])?;
945 Ok(Command::HKeys { key })
946}
947
948fn parse_hvals(args: &[Frame]) -> Result<Command, ProtocolError> {
949 if args.len() != 1 {
950 return Err(ProtocolError::WrongArity("HVALS".into()));
951 }
952 let key = extract_string(&args[0])?;
953 Ok(Command::HVals { key })
954}
955
956fn parse_hmget(args: &[Frame]) -> Result<Command, ProtocolError> {
957 if args.len() < 2 {
958 return Err(ProtocolError::WrongArity("HMGET".into()));
959 }
960 let key = extract_string(&args[0])?;
961 let fields = args[1..]
962 .iter()
963 .map(extract_string)
964 .collect::<Result<Vec<_>, _>>()?;
965 Ok(Command::HMGet { key, fields })
966}
967
968fn parse_sadd(args: &[Frame]) -> Result<Command, ProtocolError> {
971 if args.len() < 2 {
972 return Err(ProtocolError::WrongArity("SADD".into()));
973 }
974 let key = extract_string(&args[0])?;
975 let members = args[1..]
976 .iter()
977 .map(extract_string)
978 .collect::<Result<Vec<_>, _>>()?;
979 Ok(Command::SAdd { key, members })
980}
981
982fn parse_srem(args: &[Frame]) -> Result<Command, ProtocolError> {
983 if args.len() < 2 {
984 return Err(ProtocolError::WrongArity("SREM".into()));
985 }
986 let key = extract_string(&args[0])?;
987 let members = args[1..]
988 .iter()
989 .map(extract_string)
990 .collect::<Result<Vec<_>, _>>()?;
991 Ok(Command::SRem { key, members })
992}
993
994fn parse_smembers(args: &[Frame]) -> Result<Command, ProtocolError> {
995 if args.len() != 1 {
996 return Err(ProtocolError::WrongArity("SMEMBERS".into()));
997 }
998 let key = extract_string(&args[0])?;
999 Ok(Command::SMembers { key })
1000}
1001
1002fn parse_sismember(args: &[Frame]) -> Result<Command, ProtocolError> {
1003 if args.len() != 2 {
1004 return Err(ProtocolError::WrongArity("SISMEMBER".into()));
1005 }
1006 let key = extract_string(&args[0])?;
1007 let member = extract_string(&args[1])?;
1008 Ok(Command::SIsMember { key, member })
1009}
1010
1011fn parse_scard(args: &[Frame]) -> Result<Command, ProtocolError> {
1012 if args.len() != 1 {
1013 return Err(ProtocolError::WrongArity("SCARD".into()));
1014 }
1015 let key = extract_string(&args[0])?;
1016 Ok(Command::SCard { key })
1017}
1018
1019#[cfg(test)]
1020mod tests {
1021 use super::*;
1022
1023 fn cmd(parts: &[&str]) -> Frame {
1025 Frame::Array(
1026 parts
1027 .iter()
1028 .map(|s| Frame::Bulk(Bytes::from(s.to_string())))
1029 .collect(),
1030 )
1031 }
1032
1033 #[test]
1036 fn ping_no_args() {
1037 assert_eq!(
1038 Command::from_frame(cmd(&["PING"])).unwrap(),
1039 Command::Ping(None),
1040 );
1041 }
1042
1043 #[test]
1044 fn ping_with_message() {
1045 assert_eq!(
1046 Command::from_frame(cmd(&["PING", "hello"])).unwrap(),
1047 Command::Ping(Some(Bytes::from("hello"))),
1048 );
1049 }
1050
1051 #[test]
1052 fn ping_case_insensitive() {
1053 assert_eq!(
1054 Command::from_frame(cmd(&["ping"])).unwrap(),
1055 Command::Ping(None),
1056 );
1057 assert_eq!(
1058 Command::from_frame(cmd(&["Ping"])).unwrap(),
1059 Command::Ping(None),
1060 );
1061 }
1062
1063 #[test]
1064 fn ping_too_many_args() {
1065 let err = Command::from_frame(cmd(&["PING", "a", "b"])).unwrap_err();
1066 assert!(matches!(err, ProtocolError::WrongArity(_)));
1067 }
1068
1069 #[test]
1072 fn echo() {
1073 assert_eq!(
1074 Command::from_frame(cmd(&["ECHO", "test"])).unwrap(),
1075 Command::Echo(Bytes::from("test")),
1076 );
1077 }
1078
1079 #[test]
1080 fn echo_missing_arg() {
1081 let err = Command::from_frame(cmd(&["ECHO"])).unwrap_err();
1082 assert!(matches!(err, ProtocolError::WrongArity(_)));
1083 }
1084
1085 #[test]
1088 fn get_basic() {
1089 assert_eq!(
1090 Command::from_frame(cmd(&["GET", "mykey"])).unwrap(),
1091 Command::Get {
1092 key: "mykey".into()
1093 },
1094 );
1095 }
1096
1097 #[test]
1098 fn get_no_args() {
1099 let err = Command::from_frame(cmd(&["GET"])).unwrap_err();
1100 assert!(matches!(err, ProtocolError::WrongArity(_)));
1101 }
1102
1103 #[test]
1104 fn get_too_many_args() {
1105 let err = Command::from_frame(cmd(&["GET", "a", "b"])).unwrap_err();
1106 assert!(matches!(err, ProtocolError::WrongArity(_)));
1107 }
1108
1109 #[test]
1110 fn get_case_insensitive() {
1111 assert_eq!(
1112 Command::from_frame(cmd(&["get", "k"])).unwrap(),
1113 Command::Get { key: "k".into() },
1114 );
1115 }
1116
1117 #[test]
1120 fn set_basic() {
1121 assert_eq!(
1122 Command::from_frame(cmd(&["SET", "key", "value"])).unwrap(),
1123 Command::Set {
1124 key: "key".into(),
1125 value: Bytes::from("value"),
1126 expire: None,
1127 nx: false,
1128 xx: false,
1129 },
1130 );
1131 }
1132
1133 #[test]
1134 fn set_with_ex() {
1135 assert_eq!(
1136 Command::from_frame(cmd(&["SET", "key", "val", "EX", "10"])).unwrap(),
1137 Command::Set {
1138 key: "key".into(),
1139 value: Bytes::from("val"),
1140 expire: Some(SetExpire::Ex(10)),
1141 nx: false,
1142 xx: false,
1143 },
1144 );
1145 }
1146
1147 #[test]
1148 fn set_with_px() {
1149 assert_eq!(
1150 Command::from_frame(cmd(&["SET", "key", "val", "PX", "5000"])).unwrap(),
1151 Command::Set {
1152 key: "key".into(),
1153 value: Bytes::from("val"),
1154 expire: Some(SetExpire::Px(5000)),
1155 nx: false,
1156 xx: false,
1157 },
1158 );
1159 }
1160
1161 #[test]
1162 fn set_ex_case_insensitive() {
1163 assert_eq!(
1164 Command::from_frame(cmd(&["set", "k", "v", "ex", "5"])).unwrap(),
1165 Command::Set {
1166 key: "k".into(),
1167 value: Bytes::from("v"),
1168 expire: Some(SetExpire::Ex(5)),
1169 nx: false,
1170 xx: false,
1171 },
1172 );
1173 }
1174
1175 #[test]
1176 fn set_nx_flag() {
1177 assert_eq!(
1178 Command::from_frame(cmd(&["SET", "key", "val", "NX"])).unwrap(),
1179 Command::Set {
1180 key: "key".into(),
1181 value: Bytes::from("val"),
1182 expire: None,
1183 nx: true,
1184 xx: false,
1185 },
1186 );
1187 }
1188
1189 #[test]
1190 fn set_xx_flag() {
1191 assert_eq!(
1192 Command::from_frame(cmd(&["SET", "key", "val", "XX"])).unwrap(),
1193 Command::Set {
1194 key: "key".into(),
1195 value: Bytes::from("val"),
1196 expire: None,
1197 nx: false,
1198 xx: true,
1199 },
1200 );
1201 }
1202
1203 #[test]
1204 fn set_nx_with_ex() {
1205 assert_eq!(
1206 Command::from_frame(cmd(&["SET", "key", "val", "EX", "10", "NX"])).unwrap(),
1207 Command::Set {
1208 key: "key".into(),
1209 value: Bytes::from("val"),
1210 expire: Some(SetExpire::Ex(10)),
1211 nx: true,
1212 xx: false,
1213 },
1214 );
1215 }
1216
1217 #[test]
1218 fn set_nx_before_ex() {
1219 assert_eq!(
1220 Command::from_frame(cmd(&["SET", "key", "val", "NX", "PX", "5000"])).unwrap(),
1221 Command::Set {
1222 key: "key".into(),
1223 value: Bytes::from("val"),
1224 expire: Some(SetExpire::Px(5000)),
1225 nx: true,
1226 xx: false,
1227 },
1228 );
1229 }
1230
1231 #[test]
1232 fn set_nx_xx_conflict() {
1233 let err = Command::from_frame(cmd(&["SET", "k", "v", "NX", "XX"])).unwrap_err();
1234 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1235 }
1236
1237 #[test]
1238 fn set_nx_case_insensitive() {
1239 assert_eq!(
1240 Command::from_frame(cmd(&["set", "k", "v", "nx"])).unwrap(),
1241 Command::Set {
1242 key: "k".into(),
1243 value: Bytes::from("v"),
1244 expire: None,
1245 nx: true,
1246 xx: false,
1247 },
1248 );
1249 }
1250
1251 #[test]
1252 fn set_missing_value() {
1253 let err = Command::from_frame(cmd(&["SET", "key"])).unwrap_err();
1254 assert!(matches!(err, ProtocolError::WrongArity(_)));
1255 }
1256
1257 #[test]
1258 fn set_invalid_expire_value() {
1259 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX", "notanum"])).unwrap_err();
1260 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1261 }
1262
1263 #[test]
1264 fn set_zero_expire() {
1265 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX", "0"])).unwrap_err();
1266 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1267 }
1268
1269 #[test]
1270 fn set_unknown_flag() {
1271 let err = Command::from_frame(cmd(&["SET", "k", "v", "ZZ", "10"])).unwrap_err();
1272 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1273 }
1274
1275 #[test]
1276 fn set_incomplete_expire() {
1277 let err = Command::from_frame(cmd(&["SET", "k", "v", "EX"])).unwrap_err();
1279 assert!(matches!(err, ProtocolError::WrongArity(_)));
1280 }
1281
1282 #[test]
1285 fn del_single() {
1286 assert_eq!(
1287 Command::from_frame(cmd(&["DEL", "key"])).unwrap(),
1288 Command::Del {
1289 keys: vec!["key".into()]
1290 },
1291 );
1292 }
1293
1294 #[test]
1295 fn del_multiple() {
1296 assert_eq!(
1297 Command::from_frame(cmd(&["DEL", "a", "b", "c"])).unwrap(),
1298 Command::Del {
1299 keys: vec!["a".into(), "b".into(), "c".into()]
1300 },
1301 );
1302 }
1303
1304 #[test]
1305 fn del_no_args() {
1306 let err = Command::from_frame(cmd(&["DEL"])).unwrap_err();
1307 assert!(matches!(err, ProtocolError::WrongArity(_)));
1308 }
1309
1310 #[test]
1313 fn exists_single() {
1314 assert_eq!(
1315 Command::from_frame(cmd(&["EXISTS", "key"])).unwrap(),
1316 Command::Exists {
1317 keys: vec!["key".into()]
1318 },
1319 );
1320 }
1321
1322 #[test]
1323 fn exists_multiple() {
1324 assert_eq!(
1325 Command::from_frame(cmd(&["EXISTS", "a", "b"])).unwrap(),
1326 Command::Exists {
1327 keys: vec!["a".into(), "b".into()]
1328 },
1329 );
1330 }
1331
1332 #[test]
1333 fn exists_no_args() {
1334 let err = Command::from_frame(cmd(&["EXISTS"])).unwrap_err();
1335 assert!(matches!(err, ProtocolError::WrongArity(_)));
1336 }
1337
1338 #[test]
1341 fn mget_single() {
1342 assert_eq!(
1343 Command::from_frame(cmd(&["MGET", "key"])).unwrap(),
1344 Command::MGet {
1345 keys: vec!["key".into()]
1346 },
1347 );
1348 }
1349
1350 #[test]
1351 fn mget_multiple() {
1352 assert_eq!(
1353 Command::from_frame(cmd(&["MGET", "a", "b", "c"])).unwrap(),
1354 Command::MGet {
1355 keys: vec!["a".into(), "b".into(), "c".into()]
1356 },
1357 );
1358 }
1359
1360 #[test]
1361 fn mget_no_args() {
1362 let err = Command::from_frame(cmd(&["MGET"])).unwrap_err();
1363 assert!(matches!(err, ProtocolError::WrongArity(_)));
1364 }
1365
1366 #[test]
1369 fn mset_single_pair() {
1370 assert_eq!(
1371 Command::from_frame(cmd(&["MSET", "key", "val"])).unwrap(),
1372 Command::MSet {
1373 pairs: vec![("key".into(), Bytes::from("val"))]
1374 },
1375 );
1376 }
1377
1378 #[test]
1379 fn mset_multiple_pairs() {
1380 assert_eq!(
1381 Command::from_frame(cmd(&["MSET", "a", "1", "b", "2"])).unwrap(),
1382 Command::MSet {
1383 pairs: vec![
1384 ("a".into(), Bytes::from("1")),
1385 ("b".into(), Bytes::from("2")),
1386 ]
1387 },
1388 );
1389 }
1390
1391 #[test]
1392 fn mset_no_args() {
1393 let err = Command::from_frame(cmd(&["MSET"])).unwrap_err();
1394 assert!(matches!(err, ProtocolError::WrongArity(_)));
1395 }
1396
1397 #[test]
1398 fn mset_odd_args() {
1399 let err = Command::from_frame(cmd(&["MSET", "a", "1", "b"])).unwrap_err();
1401 assert!(matches!(err, ProtocolError::WrongArity(_)));
1402 }
1403
1404 #[test]
1407 fn expire_basic() {
1408 assert_eq!(
1409 Command::from_frame(cmd(&["EXPIRE", "key", "60"])).unwrap(),
1410 Command::Expire {
1411 key: "key".into(),
1412 seconds: 60,
1413 },
1414 );
1415 }
1416
1417 #[test]
1418 fn expire_wrong_arity() {
1419 let err = Command::from_frame(cmd(&["EXPIRE", "key"])).unwrap_err();
1420 assert!(matches!(err, ProtocolError::WrongArity(_)));
1421 }
1422
1423 #[test]
1424 fn expire_invalid_seconds() {
1425 let err = Command::from_frame(cmd(&["EXPIRE", "key", "abc"])).unwrap_err();
1426 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1427 }
1428
1429 #[test]
1430 fn expire_zero_seconds() {
1431 let err = Command::from_frame(cmd(&["EXPIRE", "key", "0"])).unwrap_err();
1432 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1433 }
1434
1435 #[test]
1438 fn ttl_basic() {
1439 assert_eq!(
1440 Command::from_frame(cmd(&["TTL", "key"])).unwrap(),
1441 Command::Ttl { key: "key".into() },
1442 );
1443 }
1444
1445 #[test]
1446 fn ttl_wrong_arity() {
1447 let err = Command::from_frame(cmd(&["TTL"])).unwrap_err();
1448 assert!(matches!(err, ProtocolError::WrongArity(_)));
1449 }
1450
1451 #[test]
1454 fn dbsize_basic() {
1455 assert_eq!(
1456 Command::from_frame(cmd(&["DBSIZE"])).unwrap(),
1457 Command::DbSize,
1458 );
1459 }
1460
1461 #[test]
1462 fn dbsize_case_insensitive() {
1463 assert_eq!(
1464 Command::from_frame(cmd(&["dbsize"])).unwrap(),
1465 Command::DbSize,
1466 );
1467 }
1468
1469 #[test]
1470 fn dbsize_extra_args() {
1471 let err = Command::from_frame(cmd(&["DBSIZE", "extra"])).unwrap_err();
1472 assert!(matches!(err, ProtocolError::WrongArity(_)));
1473 }
1474
1475 #[test]
1478 fn info_no_section() {
1479 assert_eq!(
1480 Command::from_frame(cmd(&["INFO"])).unwrap(),
1481 Command::Info { section: None },
1482 );
1483 }
1484
1485 #[test]
1486 fn info_with_section() {
1487 assert_eq!(
1488 Command::from_frame(cmd(&["INFO", "keyspace"])).unwrap(),
1489 Command::Info {
1490 section: Some("keyspace".into())
1491 },
1492 );
1493 }
1494
1495 #[test]
1496 fn info_too_many_args() {
1497 let err = Command::from_frame(cmd(&["INFO", "a", "b"])).unwrap_err();
1498 assert!(matches!(err, ProtocolError::WrongArity(_)));
1499 }
1500
1501 #[test]
1504 fn bgsave_basic() {
1505 assert_eq!(
1506 Command::from_frame(cmd(&["BGSAVE"])).unwrap(),
1507 Command::BgSave,
1508 );
1509 }
1510
1511 #[test]
1512 fn bgsave_case_insensitive() {
1513 assert_eq!(
1514 Command::from_frame(cmd(&["bgsave"])).unwrap(),
1515 Command::BgSave,
1516 );
1517 }
1518
1519 #[test]
1520 fn bgsave_extra_args() {
1521 let err = Command::from_frame(cmd(&["BGSAVE", "extra"])).unwrap_err();
1522 assert!(matches!(err, ProtocolError::WrongArity(_)));
1523 }
1524
1525 #[test]
1528 fn bgrewriteaof_basic() {
1529 assert_eq!(
1530 Command::from_frame(cmd(&["BGREWRITEAOF"])).unwrap(),
1531 Command::BgRewriteAof,
1532 );
1533 }
1534
1535 #[test]
1536 fn bgrewriteaof_case_insensitive() {
1537 assert_eq!(
1538 Command::from_frame(cmd(&["bgrewriteaof"])).unwrap(),
1539 Command::BgRewriteAof,
1540 );
1541 }
1542
1543 #[test]
1544 fn bgrewriteaof_extra_args() {
1545 let err = Command::from_frame(cmd(&["BGREWRITEAOF", "extra"])).unwrap_err();
1546 assert!(matches!(err, ProtocolError::WrongArity(_)));
1547 }
1548
1549 #[test]
1552 fn flushdb_basic() {
1553 assert_eq!(
1554 Command::from_frame(cmd(&["FLUSHDB"])).unwrap(),
1555 Command::FlushDb,
1556 );
1557 }
1558
1559 #[test]
1560 fn flushdb_case_insensitive() {
1561 assert_eq!(
1562 Command::from_frame(cmd(&["flushdb"])).unwrap(),
1563 Command::FlushDb,
1564 );
1565 }
1566
1567 #[test]
1568 fn flushdb_extra_args() {
1569 let err = Command::from_frame(cmd(&["FLUSHDB", "extra"])).unwrap_err();
1570 assert!(matches!(err, ProtocolError::WrongArity(_)));
1571 }
1572
1573 #[test]
1576 fn lpush_single() {
1577 assert_eq!(
1578 Command::from_frame(cmd(&["LPUSH", "list", "val"])).unwrap(),
1579 Command::LPush {
1580 key: "list".into(),
1581 values: vec![Bytes::from("val")],
1582 },
1583 );
1584 }
1585
1586 #[test]
1587 fn lpush_multiple() {
1588 assert_eq!(
1589 Command::from_frame(cmd(&["LPUSH", "list", "a", "b", "c"])).unwrap(),
1590 Command::LPush {
1591 key: "list".into(),
1592 values: vec![Bytes::from("a"), Bytes::from("b"), Bytes::from("c")],
1593 },
1594 );
1595 }
1596
1597 #[test]
1598 fn lpush_no_value() {
1599 let err = Command::from_frame(cmd(&["LPUSH", "key"])).unwrap_err();
1600 assert!(matches!(err, ProtocolError::WrongArity(_)));
1601 }
1602
1603 #[test]
1604 fn lpush_case_insensitive() {
1605 assert!(matches!(
1606 Command::from_frame(cmd(&["lpush", "k", "v"])).unwrap(),
1607 Command::LPush { .. }
1608 ));
1609 }
1610
1611 #[test]
1614 fn rpush_single() {
1615 assert_eq!(
1616 Command::from_frame(cmd(&["RPUSH", "list", "val"])).unwrap(),
1617 Command::RPush {
1618 key: "list".into(),
1619 values: vec![Bytes::from("val")],
1620 },
1621 );
1622 }
1623
1624 #[test]
1625 fn rpush_no_value() {
1626 let err = Command::from_frame(cmd(&["RPUSH", "key"])).unwrap_err();
1627 assert!(matches!(err, ProtocolError::WrongArity(_)));
1628 }
1629
1630 #[test]
1633 fn lpop_basic() {
1634 assert_eq!(
1635 Command::from_frame(cmd(&["LPOP", "list"])).unwrap(),
1636 Command::LPop { key: "list".into() },
1637 );
1638 }
1639
1640 #[test]
1641 fn lpop_wrong_arity() {
1642 let err = Command::from_frame(cmd(&["LPOP"])).unwrap_err();
1643 assert!(matches!(err, ProtocolError::WrongArity(_)));
1644 }
1645
1646 #[test]
1649 fn rpop_basic() {
1650 assert_eq!(
1651 Command::from_frame(cmd(&["RPOP", "list"])).unwrap(),
1652 Command::RPop { key: "list".into() },
1653 );
1654 }
1655
1656 #[test]
1659 fn lrange_basic() {
1660 assert_eq!(
1661 Command::from_frame(cmd(&["LRANGE", "list", "0", "-1"])).unwrap(),
1662 Command::LRange {
1663 key: "list".into(),
1664 start: 0,
1665 stop: -1,
1666 },
1667 );
1668 }
1669
1670 #[test]
1671 fn lrange_wrong_arity() {
1672 let err = Command::from_frame(cmd(&["LRANGE", "list", "0"])).unwrap_err();
1673 assert!(matches!(err, ProtocolError::WrongArity(_)));
1674 }
1675
1676 #[test]
1677 fn lrange_invalid_index() {
1678 let err = Command::from_frame(cmd(&["LRANGE", "list", "abc", "0"])).unwrap_err();
1679 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1680 }
1681
1682 #[test]
1685 fn llen_basic() {
1686 assert_eq!(
1687 Command::from_frame(cmd(&["LLEN", "list"])).unwrap(),
1688 Command::LLen { key: "list".into() },
1689 );
1690 }
1691
1692 #[test]
1693 fn llen_wrong_arity() {
1694 let err = Command::from_frame(cmd(&["LLEN"])).unwrap_err();
1695 assert!(matches!(err, ProtocolError::WrongArity(_)));
1696 }
1697
1698 #[test]
1701 fn type_basic() {
1702 assert_eq!(
1703 Command::from_frame(cmd(&["TYPE", "key"])).unwrap(),
1704 Command::Type { key: "key".into() },
1705 );
1706 }
1707
1708 #[test]
1709 fn type_wrong_arity() {
1710 let err = Command::from_frame(cmd(&["TYPE"])).unwrap_err();
1711 assert!(matches!(err, ProtocolError::WrongArity(_)));
1712 }
1713
1714 #[test]
1715 fn type_case_insensitive() {
1716 assert!(matches!(
1717 Command::from_frame(cmd(&["type", "k"])).unwrap(),
1718 Command::Type { .. }
1719 ));
1720 }
1721
1722 #[test]
1725 fn unknown_command() {
1726 assert_eq!(
1727 Command::from_frame(cmd(&["FOOBAR", "arg"])).unwrap(),
1728 Command::Unknown("FOOBAR".into()),
1729 );
1730 }
1731
1732 #[test]
1733 fn non_array_frame() {
1734 let err = Command::from_frame(Frame::Simple("PING".into())).unwrap_err();
1735 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1736 }
1737
1738 #[test]
1739 fn empty_array() {
1740 let err = Command::from_frame(Frame::Array(vec![])).unwrap_err();
1741 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1742 }
1743
1744 #[test]
1747 fn zadd_basic() {
1748 let parsed = Command::from_frame(cmd(&["ZADD", "board", "100", "alice"])).unwrap();
1749 match parsed {
1750 Command::ZAdd {
1751 key,
1752 flags,
1753 members,
1754 } => {
1755 assert_eq!(key, "board");
1756 assert_eq!(flags, ZAddFlags::default());
1757 assert_eq!(members, vec![(100.0, "alice".into())]);
1758 }
1759 other => panic!("expected ZAdd, got {other:?}"),
1760 }
1761 }
1762
1763 #[test]
1764 fn zadd_multiple_members() {
1765 let parsed =
1766 Command::from_frame(cmd(&["ZADD", "board", "100", "alice", "200", "bob"])).unwrap();
1767 match parsed {
1768 Command::ZAdd { members, .. } => {
1769 assert_eq!(members.len(), 2);
1770 assert_eq!(members[0], (100.0, "alice".into()));
1771 assert_eq!(members[1], (200.0, "bob".into()));
1772 }
1773 other => panic!("expected ZAdd, got {other:?}"),
1774 }
1775 }
1776
1777 #[test]
1778 fn zadd_with_flags() {
1779 let parsed = Command::from_frame(cmd(&["ZADD", "z", "NX", "CH", "100", "alice"])).unwrap();
1780 match parsed {
1781 Command::ZAdd { flags, .. } => {
1782 assert!(flags.nx);
1783 assert!(flags.ch);
1784 assert!(!flags.xx);
1785 assert!(!flags.gt);
1786 assert!(!flags.lt);
1787 }
1788 other => panic!("expected ZAdd, got {other:?}"),
1789 }
1790 }
1791
1792 #[test]
1793 fn zadd_gt_flag() {
1794 let parsed = Command::from_frame(cmd(&["zadd", "z", "gt", "100", "alice"])).unwrap();
1795 match parsed {
1796 Command::ZAdd { flags, .. } => assert!(flags.gt),
1797 other => panic!("expected ZAdd, got {other:?}"),
1798 }
1799 }
1800
1801 #[test]
1802 fn zadd_nx_xx_conflict() {
1803 let err = Command::from_frame(cmd(&["ZADD", "z", "NX", "XX", "100", "alice"])).unwrap_err();
1804 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1805 }
1806
1807 #[test]
1808 fn zadd_gt_lt_conflict() {
1809 let err = Command::from_frame(cmd(&["ZADD", "z", "GT", "LT", "100", "alice"])).unwrap_err();
1810 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1811 }
1812
1813 #[test]
1814 fn zadd_wrong_arity() {
1815 let err = Command::from_frame(cmd(&["ZADD", "z"])).unwrap_err();
1816 assert!(matches!(err, ProtocolError::WrongArity(_)));
1817 }
1818
1819 #[test]
1820 fn zadd_odd_score_member_count() {
1821 let err = Command::from_frame(cmd(&["ZADD", "z", "100"])).unwrap_err();
1823 assert!(matches!(err, ProtocolError::WrongArity(_)));
1824 }
1825
1826 #[test]
1827 fn zadd_invalid_score() {
1828 let err = Command::from_frame(cmd(&["ZADD", "z", "notanum", "alice"])).unwrap_err();
1829 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1830 }
1831
1832 #[test]
1833 fn zadd_nan_score() {
1834 let err = Command::from_frame(cmd(&["ZADD", "z", "nan", "alice"])).unwrap_err();
1835 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1836 }
1837
1838 #[test]
1839 fn zadd_negative_score() {
1840 let parsed = Command::from_frame(cmd(&["ZADD", "z", "-50.5", "alice"])).unwrap();
1841 match parsed {
1842 Command::ZAdd { members, .. } => {
1843 assert_eq!(members[0].0, -50.5);
1844 }
1845 other => panic!("expected ZAdd, got {other:?}"),
1846 }
1847 }
1848
1849 #[test]
1852 fn zrem_basic() {
1853 assert_eq!(
1854 Command::from_frame(cmd(&["ZREM", "z", "alice"])).unwrap(),
1855 Command::ZRem {
1856 key: "z".into(),
1857 members: vec!["alice".into()],
1858 },
1859 );
1860 }
1861
1862 #[test]
1863 fn zrem_multiple() {
1864 let parsed = Command::from_frame(cmd(&["ZREM", "z", "a", "b", "c"])).unwrap();
1865 match parsed {
1866 Command::ZRem { members, .. } => assert_eq!(members.len(), 3),
1867 other => panic!("expected ZRem, got {other:?}"),
1868 }
1869 }
1870
1871 #[test]
1872 fn zrem_wrong_arity() {
1873 let err = Command::from_frame(cmd(&["ZREM", "z"])).unwrap_err();
1874 assert!(matches!(err, ProtocolError::WrongArity(_)));
1875 }
1876
1877 #[test]
1880 fn zscore_basic() {
1881 assert_eq!(
1882 Command::from_frame(cmd(&["ZSCORE", "z", "alice"])).unwrap(),
1883 Command::ZScore {
1884 key: "z".into(),
1885 member: "alice".into(),
1886 },
1887 );
1888 }
1889
1890 #[test]
1891 fn zscore_wrong_arity() {
1892 let err = Command::from_frame(cmd(&["ZSCORE", "z"])).unwrap_err();
1893 assert!(matches!(err, ProtocolError::WrongArity(_)));
1894 }
1895
1896 #[test]
1899 fn zrank_basic() {
1900 assert_eq!(
1901 Command::from_frame(cmd(&["ZRANK", "z", "alice"])).unwrap(),
1902 Command::ZRank {
1903 key: "z".into(),
1904 member: "alice".into(),
1905 },
1906 );
1907 }
1908
1909 #[test]
1910 fn zrank_wrong_arity() {
1911 let err = Command::from_frame(cmd(&["ZRANK", "z"])).unwrap_err();
1912 assert!(matches!(err, ProtocolError::WrongArity(_)));
1913 }
1914
1915 #[test]
1918 fn zcard_basic() {
1919 assert_eq!(
1920 Command::from_frame(cmd(&["ZCARD", "z"])).unwrap(),
1921 Command::ZCard { key: "z".into() },
1922 );
1923 }
1924
1925 #[test]
1926 fn zcard_wrong_arity() {
1927 let err = Command::from_frame(cmd(&["ZCARD"])).unwrap_err();
1928 assert!(matches!(err, ProtocolError::WrongArity(_)));
1929 }
1930
1931 #[test]
1934 fn zrange_basic() {
1935 assert_eq!(
1936 Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1"])).unwrap(),
1937 Command::ZRange {
1938 key: "z".into(),
1939 start: 0,
1940 stop: -1,
1941 with_scores: false,
1942 },
1943 );
1944 }
1945
1946 #[test]
1947 fn zrange_with_scores() {
1948 assert_eq!(
1949 Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1", "WITHSCORES"])).unwrap(),
1950 Command::ZRange {
1951 key: "z".into(),
1952 start: 0,
1953 stop: -1,
1954 with_scores: true,
1955 },
1956 );
1957 }
1958
1959 #[test]
1960 fn zrange_withscores_case_insensitive() {
1961 assert_eq!(
1962 Command::from_frame(cmd(&["zrange", "z", "0", "-1", "withscores"])).unwrap(),
1963 Command::ZRange {
1964 key: "z".into(),
1965 start: 0,
1966 stop: -1,
1967 with_scores: true,
1968 },
1969 );
1970 }
1971
1972 #[test]
1973 fn zrange_invalid_option() {
1974 let err = Command::from_frame(cmd(&["ZRANGE", "z", "0", "-1", "BADOPT"])).unwrap_err();
1975 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
1976 }
1977
1978 #[test]
1979 fn zrange_wrong_arity() {
1980 let err = Command::from_frame(cmd(&["ZRANGE", "z", "0"])).unwrap_err();
1981 assert!(matches!(err, ProtocolError::WrongArity(_)));
1982 }
1983
1984 #[test]
1987 fn incr_basic() {
1988 assert_eq!(
1989 Command::from_frame(cmd(&["INCR", "counter"])).unwrap(),
1990 Command::Incr {
1991 key: "counter".into()
1992 },
1993 );
1994 }
1995
1996 #[test]
1997 fn incr_wrong_arity() {
1998 let err = Command::from_frame(cmd(&["INCR"])).unwrap_err();
1999 assert!(matches!(err, ProtocolError::WrongArity(_)));
2000 }
2001
2002 #[test]
2005 fn decr_basic() {
2006 assert_eq!(
2007 Command::from_frame(cmd(&["DECR", "counter"])).unwrap(),
2008 Command::Decr {
2009 key: "counter".into()
2010 },
2011 );
2012 }
2013
2014 #[test]
2015 fn decr_wrong_arity() {
2016 let err = Command::from_frame(cmd(&["DECR"])).unwrap_err();
2017 assert!(matches!(err, ProtocolError::WrongArity(_)));
2018 }
2019
2020 #[test]
2023 fn persist_basic() {
2024 assert_eq!(
2025 Command::from_frame(cmd(&["PERSIST", "key"])).unwrap(),
2026 Command::Persist { key: "key".into() },
2027 );
2028 }
2029
2030 #[test]
2031 fn persist_case_insensitive() {
2032 assert_eq!(
2033 Command::from_frame(cmd(&["persist", "key"])).unwrap(),
2034 Command::Persist { key: "key".into() },
2035 );
2036 }
2037
2038 #[test]
2039 fn persist_wrong_arity() {
2040 let err = Command::from_frame(cmd(&["PERSIST"])).unwrap_err();
2041 assert!(matches!(err, ProtocolError::WrongArity(_)));
2042 }
2043
2044 #[test]
2047 fn pttl_basic() {
2048 assert_eq!(
2049 Command::from_frame(cmd(&["PTTL", "key"])).unwrap(),
2050 Command::Pttl { key: "key".into() },
2051 );
2052 }
2053
2054 #[test]
2055 fn pttl_wrong_arity() {
2056 let err = Command::from_frame(cmd(&["PTTL"])).unwrap_err();
2057 assert!(matches!(err, ProtocolError::WrongArity(_)));
2058 }
2059
2060 #[test]
2063 fn pexpire_basic() {
2064 assert_eq!(
2065 Command::from_frame(cmd(&["PEXPIRE", "key", "5000"])).unwrap(),
2066 Command::Pexpire {
2067 key: "key".into(),
2068 milliseconds: 5000,
2069 },
2070 );
2071 }
2072
2073 #[test]
2074 fn pexpire_wrong_arity() {
2075 let err = Command::from_frame(cmd(&["PEXPIRE", "key"])).unwrap_err();
2076 assert!(matches!(err, ProtocolError::WrongArity(_)));
2077 }
2078
2079 #[test]
2080 fn pexpire_zero_millis() {
2081 let err = Command::from_frame(cmd(&["PEXPIRE", "key", "0"])).unwrap_err();
2082 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2083 }
2084
2085 #[test]
2086 fn pexpire_invalid_millis() {
2087 let err = Command::from_frame(cmd(&["PEXPIRE", "key", "notanum"])).unwrap_err();
2088 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2089 }
2090
2091 #[test]
2094 fn scan_basic() {
2095 assert_eq!(
2096 Command::from_frame(cmd(&["SCAN", "0"])).unwrap(),
2097 Command::Scan {
2098 cursor: 0,
2099 pattern: None,
2100 count: None,
2101 },
2102 );
2103 }
2104
2105 #[test]
2106 fn scan_with_match() {
2107 assert_eq!(
2108 Command::from_frame(cmd(&["SCAN", "0", "MATCH", "user:*"])).unwrap(),
2109 Command::Scan {
2110 cursor: 0,
2111 pattern: Some("user:*".into()),
2112 count: None,
2113 },
2114 );
2115 }
2116
2117 #[test]
2118 fn scan_with_count() {
2119 assert_eq!(
2120 Command::from_frame(cmd(&["SCAN", "42", "COUNT", "100"])).unwrap(),
2121 Command::Scan {
2122 cursor: 42,
2123 pattern: None,
2124 count: Some(100),
2125 },
2126 );
2127 }
2128
2129 #[test]
2130 fn scan_with_match_and_count() {
2131 assert_eq!(
2132 Command::from_frame(cmd(&["SCAN", "0", "MATCH", "*:data", "COUNT", "50"])).unwrap(),
2133 Command::Scan {
2134 cursor: 0,
2135 pattern: Some("*:data".into()),
2136 count: Some(50),
2137 },
2138 );
2139 }
2140
2141 #[test]
2142 fn scan_count_before_match() {
2143 assert_eq!(
2144 Command::from_frame(cmd(&["SCAN", "0", "COUNT", "10", "MATCH", "foo*"])).unwrap(),
2145 Command::Scan {
2146 cursor: 0,
2147 pattern: Some("foo*".into()),
2148 count: Some(10),
2149 },
2150 );
2151 }
2152
2153 #[test]
2154 fn scan_case_insensitive() {
2155 assert_eq!(
2156 Command::from_frame(cmd(&["scan", "0", "match", "x*", "count", "5"])).unwrap(),
2157 Command::Scan {
2158 cursor: 0,
2159 pattern: Some("x*".into()),
2160 count: Some(5),
2161 },
2162 );
2163 }
2164
2165 #[test]
2166 fn scan_wrong_arity() {
2167 let err = Command::from_frame(cmd(&["SCAN"])).unwrap_err();
2168 assert!(matches!(err, ProtocolError::WrongArity(_)));
2169 }
2170
2171 #[test]
2172 fn scan_invalid_cursor() {
2173 let err = Command::from_frame(cmd(&["SCAN", "notanum"])).unwrap_err();
2174 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2175 }
2176
2177 #[test]
2178 fn scan_invalid_count() {
2179 let err = Command::from_frame(cmd(&["SCAN", "0", "COUNT", "bad"])).unwrap_err();
2180 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2181 }
2182
2183 #[test]
2184 fn scan_unknown_flag() {
2185 let err = Command::from_frame(cmd(&["SCAN", "0", "BADOPT", "val"])).unwrap_err();
2186 assert!(matches!(err, ProtocolError::InvalidCommandFrame(_)));
2187 }
2188
2189 #[test]
2190 fn scan_match_missing_pattern() {
2191 let err = Command::from_frame(cmd(&["SCAN", "0", "MATCH"])).unwrap_err();
2192 assert!(matches!(err, ProtocolError::WrongArity(_)));
2193 }
2194
2195 #[test]
2196 fn scan_count_missing_value() {
2197 let err = Command::from_frame(cmd(&["SCAN", "0", "COUNT"])).unwrap_err();
2198 assert!(matches!(err, ProtocolError::WrongArity(_)));
2199 }
2200
2201 #[test]
2204 fn hset_single_field() {
2205 assert_eq!(
2206 Command::from_frame(cmd(&["HSET", "h", "field", "value"])).unwrap(),
2207 Command::HSet {
2208 key: "h".into(),
2209 fields: vec![("field".into(), Bytes::from("value"))],
2210 },
2211 );
2212 }
2213
2214 #[test]
2215 fn hset_multiple_fields() {
2216 let parsed = Command::from_frame(cmd(&["HSET", "h", "f1", "v1", "f2", "v2"])).unwrap();
2217 match parsed {
2218 Command::HSet { key, fields } => {
2219 assert_eq!(key, "h");
2220 assert_eq!(fields.len(), 2);
2221 }
2222 other => panic!("expected HSet, got {other:?}"),
2223 }
2224 }
2225
2226 #[test]
2227 fn hset_wrong_arity() {
2228 let err = Command::from_frame(cmd(&["HSET", "h"])).unwrap_err();
2229 assert!(matches!(err, ProtocolError::WrongArity(_)));
2230 let err = Command::from_frame(cmd(&["HSET", "h", "f"])).unwrap_err();
2231 assert!(matches!(err, ProtocolError::WrongArity(_)));
2232 }
2233
2234 #[test]
2235 fn hget_basic() {
2236 assert_eq!(
2237 Command::from_frame(cmd(&["HGET", "h", "field"])).unwrap(),
2238 Command::HGet {
2239 key: "h".into(),
2240 field: "field".into(),
2241 },
2242 );
2243 }
2244
2245 #[test]
2246 fn hget_wrong_arity() {
2247 let err = Command::from_frame(cmd(&["HGET", "h"])).unwrap_err();
2248 assert!(matches!(err, ProtocolError::WrongArity(_)));
2249 }
2250
2251 #[test]
2252 fn hgetall_basic() {
2253 assert_eq!(
2254 Command::from_frame(cmd(&["HGETALL", "h"])).unwrap(),
2255 Command::HGetAll { key: "h".into() },
2256 );
2257 }
2258
2259 #[test]
2260 fn hgetall_wrong_arity() {
2261 let err = Command::from_frame(cmd(&["HGETALL"])).unwrap_err();
2262 assert!(matches!(err, ProtocolError::WrongArity(_)));
2263 }
2264
2265 #[test]
2266 fn hdel_single() {
2267 assert_eq!(
2268 Command::from_frame(cmd(&["HDEL", "h", "f"])).unwrap(),
2269 Command::HDel {
2270 key: "h".into(),
2271 fields: vec!["f".into()],
2272 },
2273 );
2274 }
2275
2276 #[test]
2277 fn hdel_multiple() {
2278 let parsed = Command::from_frame(cmd(&["HDEL", "h", "f1", "f2", "f3"])).unwrap();
2279 match parsed {
2280 Command::HDel { fields, .. } => assert_eq!(fields.len(), 3),
2281 other => panic!("expected HDel, got {other:?}"),
2282 }
2283 }
2284
2285 #[test]
2286 fn hdel_wrong_arity() {
2287 let err = Command::from_frame(cmd(&["HDEL", "h"])).unwrap_err();
2288 assert!(matches!(err, ProtocolError::WrongArity(_)));
2289 }
2290
2291 #[test]
2292 fn hexists_basic() {
2293 assert_eq!(
2294 Command::from_frame(cmd(&["HEXISTS", "h", "f"])).unwrap(),
2295 Command::HExists {
2296 key: "h".into(),
2297 field: "f".into(),
2298 },
2299 );
2300 }
2301
2302 #[test]
2303 fn hlen_basic() {
2304 assert_eq!(
2305 Command::from_frame(cmd(&["HLEN", "h"])).unwrap(),
2306 Command::HLen { key: "h".into() },
2307 );
2308 }
2309
2310 #[test]
2311 fn hincrby_basic() {
2312 assert_eq!(
2313 Command::from_frame(cmd(&["HINCRBY", "h", "f", "5"])).unwrap(),
2314 Command::HIncrBy {
2315 key: "h".into(),
2316 field: "f".into(),
2317 delta: 5,
2318 },
2319 );
2320 }
2321
2322 #[test]
2323 fn hincrby_negative() {
2324 assert_eq!(
2325 Command::from_frame(cmd(&["HINCRBY", "h", "f", "-3"])).unwrap(),
2326 Command::HIncrBy {
2327 key: "h".into(),
2328 field: "f".into(),
2329 delta: -3,
2330 },
2331 );
2332 }
2333
2334 #[test]
2335 fn hincrby_wrong_arity() {
2336 let err = Command::from_frame(cmd(&["HINCRBY", "h", "f"])).unwrap_err();
2337 assert!(matches!(err, ProtocolError::WrongArity(_)));
2338 }
2339
2340 #[test]
2341 fn hkeys_basic() {
2342 assert_eq!(
2343 Command::from_frame(cmd(&["HKEYS", "h"])).unwrap(),
2344 Command::HKeys { key: "h".into() },
2345 );
2346 }
2347
2348 #[test]
2349 fn hvals_basic() {
2350 assert_eq!(
2351 Command::from_frame(cmd(&["HVALS", "h"])).unwrap(),
2352 Command::HVals { key: "h".into() },
2353 );
2354 }
2355
2356 #[test]
2357 fn hmget_basic() {
2358 assert_eq!(
2359 Command::from_frame(cmd(&["HMGET", "h", "f1", "f2"])).unwrap(),
2360 Command::HMGet {
2361 key: "h".into(),
2362 fields: vec!["f1".into(), "f2".into()],
2363 },
2364 );
2365 }
2366
2367 #[test]
2368 fn hmget_wrong_arity() {
2369 let err = Command::from_frame(cmd(&["HMGET", "h"])).unwrap_err();
2370 assert!(matches!(err, ProtocolError::WrongArity(_)));
2371 }
2372
2373 #[test]
2374 fn hash_commands_case_insensitive() {
2375 assert!(matches!(
2376 Command::from_frame(cmd(&["hset", "h", "f", "v"])).unwrap(),
2377 Command::HSet { .. }
2378 ));
2379 assert!(matches!(
2380 Command::from_frame(cmd(&["hget", "h", "f"])).unwrap(),
2381 Command::HGet { .. }
2382 ));
2383 assert!(matches!(
2384 Command::from_frame(cmd(&["hgetall", "h"])).unwrap(),
2385 Command::HGetAll { .. }
2386 ));
2387 }
2388
2389 #[test]
2392 fn sadd_single_member() {
2393 assert_eq!(
2394 Command::from_frame(cmd(&["SADD", "s", "member"])).unwrap(),
2395 Command::SAdd {
2396 key: "s".into(),
2397 members: vec!["member".into()],
2398 },
2399 );
2400 }
2401
2402 #[test]
2403 fn sadd_multiple_members() {
2404 let parsed = Command::from_frame(cmd(&["SADD", "s", "a", "b", "c"])).unwrap();
2405 match parsed {
2406 Command::SAdd { key, members } => {
2407 assert_eq!(key, "s");
2408 assert_eq!(members.len(), 3);
2409 }
2410 other => panic!("expected SAdd, got {other:?}"),
2411 }
2412 }
2413
2414 #[test]
2415 fn sadd_wrong_arity() {
2416 let err = Command::from_frame(cmd(&["SADD", "s"])).unwrap_err();
2417 assert!(matches!(err, ProtocolError::WrongArity(_)));
2418 }
2419
2420 #[test]
2421 fn srem_single_member() {
2422 assert_eq!(
2423 Command::from_frame(cmd(&["SREM", "s", "member"])).unwrap(),
2424 Command::SRem {
2425 key: "s".into(),
2426 members: vec!["member".into()],
2427 },
2428 );
2429 }
2430
2431 #[test]
2432 fn srem_multiple_members() {
2433 let parsed = Command::from_frame(cmd(&["SREM", "s", "a", "b"])).unwrap();
2434 match parsed {
2435 Command::SRem { key, members } => {
2436 assert_eq!(key, "s");
2437 assert_eq!(members.len(), 2);
2438 }
2439 other => panic!("expected SRem, got {other:?}"),
2440 }
2441 }
2442
2443 #[test]
2444 fn srem_wrong_arity() {
2445 let err = Command::from_frame(cmd(&["SREM", "s"])).unwrap_err();
2446 assert!(matches!(err, ProtocolError::WrongArity(_)));
2447 }
2448
2449 #[test]
2450 fn smembers_basic() {
2451 assert_eq!(
2452 Command::from_frame(cmd(&["SMEMBERS", "s"])).unwrap(),
2453 Command::SMembers { key: "s".into() },
2454 );
2455 }
2456
2457 #[test]
2458 fn smembers_wrong_arity() {
2459 let err = Command::from_frame(cmd(&["SMEMBERS"])).unwrap_err();
2460 assert!(matches!(err, ProtocolError::WrongArity(_)));
2461 }
2462
2463 #[test]
2464 fn sismember_basic() {
2465 assert_eq!(
2466 Command::from_frame(cmd(&["SISMEMBER", "s", "member"])).unwrap(),
2467 Command::SIsMember {
2468 key: "s".into(),
2469 member: "member".into(),
2470 },
2471 );
2472 }
2473
2474 #[test]
2475 fn sismember_wrong_arity() {
2476 let err = Command::from_frame(cmd(&["SISMEMBER", "s"])).unwrap_err();
2477 assert!(matches!(err, ProtocolError::WrongArity(_)));
2478 }
2479
2480 #[test]
2481 fn scard_basic() {
2482 assert_eq!(
2483 Command::from_frame(cmd(&["SCARD", "s"])).unwrap(),
2484 Command::SCard { key: "s".into() },
2485 );
2486 }
2487
2488 #[test]
2489 fn scard_wrong_arity() {
2490 let err = Command::from_frame(cmd(&["SCARD"])).unwrap_err();
2491 assert!(matches!(err, ProtocolError::WrongArity(_)));
2492 }
2493
2494 #[test]
2495 fn set_commands_case_insensitive() {
2496 assert!(matches!(
2497 Command::from_frame(cmd(&["sadd", "s", "m"])).unwrap(),
2498 Command::SAdd { .. }
2499 ));
2500 assert!(matches!(
2501 Command::from_frame(cmd(&["srem", "s", "m"])).unwrap(),
2502 Command::SRem { .. }
2503 ));
2504 assert!(matches!(
2505 Command::from_frame(cmd(&["smembers", "s"])).unwrap(),
2506 Command::SMembers { .. }
2507 ));
2508 }
2509}