1use crate::error::ParseError;
7use crate::value::ParseOptions;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum Command<'a> {
15 Ping,
17 Get { key: &'a [u8] },
19 Set {
21 key: &'a [u8],
22 value: &'a [u8],
23 ex: Option<u64>,
24 px: Option<u64>,
25 nx: bool,
26 xx: bool,
27 },
28 Del { key: &'a [u8] },
30 MGet { keys: Vec<&'a [u8]> },
32 Config {
34 subcommand: &'a [u8],
35 args: Vec<&'a [u8]>,
36 },
37 Cluster {
39 subcommand: &'a [u8],
40 args: Vec<&'a [u8]>,
41 },
42 Asking,
44 ReadOnly,
46 ReadWrite,
48 FlushDb,
50 FlushAll,
52 Incr { key: &'a [u8] },
54 Decr { key: &'a [u8] },
56 IncrBy { key: &'a [u8], delta: i64 },
58 DecrBy { key: &'a [u8], delta: i64 },
60 Append { key: &'a [u8], value: &'a [u8] },
62 #[cfg(feature = "resp3")]
65 Hello {
66 proto_version: Option<u8>,
68 auth: Option<(&'a [u8], &'a [u8])>,
70 client_name: Option<&'a [u8]>,
72 },
73
74 HSet {
79 key: &'a [u8],
80 fields: Vec<(&'a [u8], &'a [u8])>,
81 },
82 HGet { key: &'a [u8], field: &'a [u8] },
84 HMGet {
86 key: &'a [u8],
87 fields: Vec<&'a [u8]>,
88 },
89 HGetAll { key: &'a [u8] },
91 HDel {
93 key: &'a [u8],
94 fields: Vec<&'a [u8]>,
95 },
96 HExists { key: &'a [u8], field: &'a [u8] },
98 HLen { key: &'a [u8] },
100 HKeys { key: &'a [u8] },
102 HVals { key: &'a [u8] },
104 HSetNx {
106 key: &'a [u8],
107 field: &'a [u8],
108 value: &'a [u8],
109 },
110 HIncrBy {
112 key: &'a [u8],
113 field: &'a [u8],
114 delta: i64,
115 },
116
117 LPush {
122 key: &'a [u8],
123 values: Vec<&'a [u8]>,
124 },
125 RPush {
127 key: &'a [u8],
128 values: Vec<&'a [u8]>,
129 },
130 LPop { key: &'a [u8], count: Option<usize> },
132 RPop { key: &'a [u8], count: Option<usize> },
134 LRange {
136 key: &'a [u8],
137 start: i64,
138 stop: i64,
139 },
140 LLen { key: &'a [u8] },
142 LIndex { key: &'a [u8], index: i64 },
144 LSet {
146 key: &'a [u8],
147 index: i64,
148 value: &'a [u8],
149 },
150 LTrim {
152 key: &'a [u8],
153 start: i64,
154 stop: i64,
155 },
156 LPushX {
158 key: &'a [u8],
159 values: Vec<&'a [u8]>,
160 },
161 RPushX {
163 key: &'a [u8],
164 values: Vec<&'a [u8]>,
165 },
166
167 SAdd {
172 key: &'a [u8],
173 members: Vec<&'a [u8]>,
174 },
175 SRem {
177 key: &'a [u8],
178 members: Vec<&'a [u8]>,
179 },
180 SMembers { key: &'a [u8] },
182 SIsMember { key: &'a [u8], member: &'a [u8] },
184 SMisMember {
186 key: &'a [u8],
187 members: Vec<&'a [u8]>,
188 },
189 SCard { key: &'a [u8] },
191 SPop { key: &'a [u8], count: Option<usize> },
193 SRandMember { key: &'a [u8], count: Option<i64> },
195
196 Type { key: &'a [u8] },
201}
202
203impl<'a> Command<'a> {
204 #[inline]
218 pub fn parse(buffer: &'a [u8]) -> Result<(Self, usize), ParseError> {
219 Self::parse_with_options(buffer, &ParseOptions::default())
220 }
221
222 #[inline]
238 pub fn parse_with_options(
239 buffer: &'a [u8],
240 options: &ParseOptions,
241 ) -> Result<(Self, usize), ParseError> {
242 let mut cursor = Cursor::new(buffer, options.max_bulk_string_len);
243
244 if cursor.remaining() < 1 {
246 return Err(ParseError::Incomplete);
247 }
248 if cursor.get_u8() != b'*' {
249 return Err(ParseError::Protocol("expected array".to_string()));
250 }
251
252 let count = cursor.read_integer()?;
254 if count < 1 {
255 return Err(ParseError::Protocol(
256 "array must have at least 1 element".to_string(),
257 ));
258 }
259 const MAX_ARRAY_LEN: usize = 1024 * 1024; if count > MAX_ARRAY_LEN {
262 return Err(ParseError::Protocol("array too large".to_string()));
263 }
264
265 let cmd_name = cursor.read_bulk_string()?;
267 let cmd_str = std::str::from_utf8(cmd_name)
268 .map_err(|_| ParseError::Protocol("invalid UTF-8 in command".to_string()))?;
269
270 let command = match () {
272 _ if cmd_str.eq_ignore_ascii_case("ping") => {
273 if count != 1 {
274 return Err(ParseError::WrongArity(
275 "PING takes no arguments".to_string(),
276 ));
277 }
278 Command::Ping
279 }
280
281 _ if cmd_str.eq_ignore_ascii_case("get") => {
282 if count != 2 {
283 return Err(ParseError::WrongArity(
284 "GET requires exactly 1 argument".to_string(),
285 ));
286 }
287 let key = cursor.read_bulk_string()?;
288 Command::Get { key }
289 }
290
291 _ if cmd_str.eq_ignore_ascii_case("set") => {
292 if count < 3 {
293 return Err(ParseError::WrongArity(
294 "SET requires at least 2 arguments".to_string(),
295 ));
296 }
297 let key = cursor.read_bulk_string()?;
298 let value = cursor.read_bulk_string()?;
299
300 let mut ex = None;
301 let mut px = None;
302 let mut nx = false;
303 let mut xx = false;
304
305 let mut remaining_args = count - 3;
306 while remaining_args > 0 {
307 let option = cursor.read_bulk_string()?;
308 let option_str = std::str::from_utf8(option)
309 .map_err(|_| ParseError::Protocol("invalid UTF-8 in option".to_string()))?;
310
311 if option_str.eq_ignore_ascii_case("ex") {
312 if remaining_args < 2 {
313 return Err(ParseError::Protocol("EX requires a value".to_string()));
314 }
315 let ttl_bytes = cursor.read_bulk_string()?;
316 let ttl_str = std::str::from_utf8(ttl_bytes).map_err(|_| {
317 ParseError::Protocol("invalid UTF-8 in TTL".to_string())
318 })?;
319 let ttl_secs = ttl_str
320 .parse::<u64>()
321 .map_err(|_| ParseError::Protocol("invalid TTL value".to_string()))?;
322 ex = Some(ttl_secs);
323 remaining_args -= 2;
324 } else if option_str.eq_ignore_ascii_case("px") {
325 if remaining_args < 2 {
326 return Err(ParseError::Protocol("PX requires a value".to_string()));
327 }
328 let ttl_bytes = cursor.read_bulk_string()?;
329 let ttl_str = std::str::from_utf8(ttl_bytes).map_err(|_| {
330 ParseError::Protocol("invalid UTF-8 in TTL".to_string())
331 })?;
332 let ttl_ms = ttl_str
333 .parse::<u64>()
334 .map_err(|_| ParseError::Protocol("invalid TTL value".to_string()))?;
335 px = Some(ttl_ms);
336 remaining_args -= 2;
337 } else if option_str.eq_ignore_ascii_case("nx") {
338 nx = true;
339 remaining_args -= 1;
340 } else if option_str.eq_ignore_ascii_case("xx") {
341 xx = true;
342 remaining_args -= 1;
343 } else {
344 return Err(ParseError::Protocol(format!(
345 "unknown SET option: {}",
346 option_str
347 )));
348 }
349 }
350
351 Command::Set {
352 key,
353 value,
354 ex,
355 px,
356 nx,
357 xx,
358 }
359 }
360
361 _ if cmd_str.eq_ignore_ascii_case("del") => {
362 if count != 2 {
363 return Err(ParseError::WrongArity(
364 "DEL requires exactly 1 argument".to_string(),
365 ));
366 }
367 let key = cursor.read_bulk_string()?;
368 Command::Del { key }
369 }
370
371 _ if cmd_str.eq_ignore_ascii_case("mget") => {
372 if count < 2 {
373 return Err(ParseError::WrongArity(
374 "MGET requires at least 1 argument".to_string(),
375 ));
376 }
377 let mut keys = Vec::with_capacity(count - 1);
378 for _ in 0..(count - 1) {
379 keys.push(cursor.read_bulk_string()?);
380 }
381 Command::MGet { keys }
382 }
383
384 _ if cmd_str.eq_ignore_ascii_case("config") => {
385 if count < 2 {
386 return Err(ParseError::WrongArity(
387 "CONFIG requires at least 1 argument".to_string(),
388 ));
389 }
390 let subcommand = cursor.read_bulk_string()?;
391 let mut args = Vec::with_capacity(count - 2);
392 for _ in 0..(count - 2) {
393 args.push(cursor.read_bulk_string()?);
394 }
395 Command::Config { subcommand, args }
396 }
397
398 _ if cmd_str.eq_ignore_ascii_case("cluster") => {
399 if count < 2 {
400 return Err(ParseError::WrongArity(
401 "CLUSTER requires at least 1 argument".to_string(),
402 ));
403 }
404 let subcommand = cursor.read_bulk_string()?;
405 let mut args = Vec::with_capacity(count - 2);
406 for _ in 0..(count - 2) {
407 args.push(cursor.read_bulk_string()?);
408 }
409 Command::Cluster { subcommand, args }
410 }
411
412 _ if cmd_str.eq_ignore_ascii_case("asking") => {
413 if count != 1 {
414 return Err(ParseError::WrongArity(
415 "ASKING takes no arguments".to_string(),
416 ));
417 }
418 Command::Asking
419 }
420
421 _ if cmd_str.eq_ignore_ascii_case("readonly") => {
422 if count != 1 {
423 return Err(ParseError::WrongArity(
424 "READONLY takes no arguments".to_string(),
425 ));
426 }
427 Command::ReadOnly
428 }
429
430 _ if cmd_str.eq_ignore_ascii_case("readwrite") => {
431 if count != 1 {
432 return Err(ParseError::WrongArity(
433 "READWRITE takes no arguments".to_string(),
434 ));
435 }
436 Command::ReadWrite
437 }
438
439 _ if cmd_str.eq_ignore_ascii_case("flushdb") => Command::FlushDb,
440 _ if cmd_str.eq_ignore_ascii_case("flushall") => Command::FlushAll,
441
442 _ if cmd_str.eq_ignore_ascii_case("incr") => {
443 if count != 2 {
444 return Err(ParseError::WrongArity(
445 "INCR requires exactly 1 argument".to_string(),
446 ));
447 }
448 let key = cursor.read_bulk_string()?;
449 Command::Incr { key }
450 }
451
452 _ if cmd_str.eq_ignore_ascii_case("decr") => {
453 if count != 2 {
454 return Err(ParseError::WrongArity(
455 "DECR requires exactly 1 argument".to_string(),
456 ));
457 }
458 let key = cursor.read_bulk_string()?;
459 Command::Decr { key }
460 }
461
462 _ if cmd_str.eq_ignore_ascii_case("incrby") => {
463 if count != 3 {
464 return Err(ParseError::WrongArity(
465 "INCRBY requires exactly 2 arguments".to_string(),
466 ));
467 }
468 let key = cursor.read_bulk_string()?;
469 let delta_bytes = cursor.read_bulk_string()?;
470 let delta_str = std::str::from_utf8(delta_bytes)
471 .map_err(|_| ParseError::Protocol("invalid UTF-8 in delta".to_string()))?;
472 let delta = delta_str
473 .parse::<i64>()
474 .map_err(|_| ParseError::Protocol("invalid delta value".to_string()))?;
475 Command::IncrBy { key, delta }
476 }
477
478 _ if cmd_str.eq_ignore_ascii_case("decrby") => {
479 if count != 3 {
480 return Err(ParseError::WrongArity(
481 "DECRBY requires exactly 2 arguments".to_string(),
482 ));
483 }
484 let key = cursor.read_bulk_string()?;
485 let delta_bytes = cursor.read_bulk_string()?;
486 let delta_str = std::str::from_utf8(delta_bytes)
487 .map_err(|_| ParseError::Protocol("invalid UTF-8 in delta".to_string()))?;
488 let delta = delta_str
489 .parse::<i64>()
490 .map_err(|_| ParseError::Protocol("invalid delta value".to_string()))?;
491 Command::DecrBy { key, delta }
492 }
493
494 _ if cmd_str.eq_ignore_ascii_case("append") => {
495 if count != 3 {
496 return Err(ParseError::WrongArity(
497 "APPEND requires exactly 2 arguments".to_string(),
498 ));
499 }
500 let key = cursor.read_bulk_string()?;
501 let value = cursor.read_bulk_string()?;
502 Command::Append { key, value }
503 }
504
505 _ if cmd_str.eq_ignore_ascii_case("hset") => {
509 if count < 4 || (count - 2) % 2 != 0 {
510 return Err(ParseError::WrongArity(
511 "HSET requires key and field-value pairs".to_string(),
512 ));
513 }
514 let key = cursor.read_bulk_string()?;
515 let mut fields = Vec::with_capacity((count - 2) / 2);
516 for _ in 0..((count - 2) / 2) {
517 let field = cursor.read_bulk_string()?;
518 let value = cursor.read_bulk_string()?;
519 fields.push((field, value));
520 }
521 Command::HSet { key, fields }
522 }
523
524 _ if cmd_str.eq_ignore_ascii_case("hget") => {
525 if count != 3 {
526 return Err(ParseError::WrongArity(
527 "HGET requires exactly 2 arguments".to_string(),
528 ));
529 }
530 let key = cursor.read_bulk_string()?;
531 let field = cursor.read_bulk_string()?;
532 Command::HGet { key, field }
533 }
534
535 _ if cmd_str.eq_ignore_ascii_case("hmget") => {
536 if count < 3 {
537 return Err(ParseError::WrongArity(
538 "HMGET requires at least 2 arguments".to_string(),
539 ));
540 }
541 let key = cursor.read_bulk_string()?;
542 let mut fields = Vec::with_capacity(count - 2);
543 for _ in 0..(count - 2) {
544 fields.push(cursor.read_bulk_string()?);
545 }
546 Command::HMGet { key, fields }
547 }
548
549 _ if cmd_str.eq_ignore_ascii_case("hgetall") => {
550 if count != 2 {
551 return Err(ParseError::WrongArity(
552 "HGETALL requires exactly 1 argument".to_string(),
553 ));
554 }
555 let key = cursor.read_bulk_string()?;
556 Command::HGetAll { key }
557 }
558
559 _ if cmd_str.eq_ignore_ascii_case("hdel") => {
560 if count < 3 {
561 return Err(ParseError::WrongArity(
562 "HDEL requires at least 2 arguments".to_string(),
563 ));
564 }
565 let key = cursor.read_bulk_string()?;
566 let mut fields = Vec::with_capacity(count - 2);
567 for _ in 0..(count - 2) {
568 fields.push(cursor.read_bulk_string()?);
569 }
570 Command::HDel { key, fields }
571 }
572
573 _ if cmd_str.eq_ignore_ascii_case("hexists") => {
574 if count != 3 {
575 return Err(ParseError::WrongArity(
576 "HEXISTS requires exactly 2 arguments".to_string(),
577 ));
578 }
579 let key = cursor.read_bulk_string()?;
580 let field = cursor.read_bulk_string()?;
581 Command::HExists { key, field }
582 }
583
584 _ if cmd_str.eq_ignore_ascii_case("hlen") => {
585 if count != 2 {
586 return Err(ParseError::WrongArity(
587 "HLEN requires exactly 1 argument".to_string(),
588 ));
589 }
590 let key = cursor.read_bulk_string()?;
591 Command::HLen { key }
592 }
593
594 _ if cmd_str.eq_ignore_ascii_case("hkeys") => {
595 if count != 2 {
596 return Err(ParseError::WrongArity(
597 "HKEYS requires exactly 1 argument".to_string(),
598 ));
599 }
600 let key = cursor.read_bulk_string()?;
601 Command::HKeys { key }
602 }
603
604 _ if cmd_str.eq_ignore_ascii_case("hvals") => {
605 if count != 2 {
606 return Err(ParseError::WrongArity(
607 "HVALS requires exactly 1 argument".to_string(),
608 ));
609 }
610 let key = cursor.read_bulk_string()?;
611 Command::HVals { key }
612 }
613
614 _ if cmd_str.eq_ignore_ascii_case("hsetnx") => {
615 if count != 4 {
616 return Err(ParseError::WrongArity(
617 "HSETNX requires exactly 3 arguments".to_string(),
618 ));
619 }
620 let key = cursor.read_bulk_string()?;
621 let field = cursor.read_bulk_string()?;
622 let value = cursor.read_bulk_string()?;
623 Command::HSetNx { key, field, value }
624 }
625
626 _ if cmd_str.eq_ignore_ascii_case("hincrby") => {
627 if count != 4 {
628 return Err(ParseError::WrongArity(
629 "HINCRBY requires exactly 3 arguments".to_string(),
630 ));
631 }
632 let key = cursor.read_bulk_string()?;
633 let field = cursor.read_bulk_string()?;
634 let delta_bytes = cursor.read_bulk_string()?;
635 let delta_str = std::str::from_utf8(delta_bytes)
636 .map_err(|_| ParseError::Protocol("invalid UTF-8 in increment".to_string()))?;
637 let delta = delta_str
638 .parse::<i64>()
639 .map_err(|_| ParseError::Protocol("invalid increment value".to_string()))?;
640 Command::HIncrBy { key, field, delta }
641 }
642
643 _ if cmd_str.eq_ignore_ascii_case("lpush") => {
647 if count < 3 {
648 return Err(ParseError::WrongArity(
649 "LPUSH requires at least 2 arguments".to_string(),
650 ));
651 }
652 let key = cursor.read_bulk_string()?;
653 let mut values = Vec::with_capacity(count - 2);
654 for _ in 0..(count - 2) {
655 values.push(cursor.read_bulk_string()?);
656 }
657 Command::LPush { key, values }
658 }
659
660 _ if cmd_str.eq_ignore_ascii_case("rpush") => {
661 if count < 3 {
662 return Err(ParseError::WrongArity(
663 "RPUSH requires at least 2 arguments".to_string(),
664 ));
665 }
666 let key = cursor.read_bulk_string()?;
667 let mut values = Vec::with_capacity(count - 2);
668 for _ in 0..(count - 2) {
669 values.push(cursor.read_bulk_string()?);
670 }
671 Command::RPush { key, values }
672 }
673
674 _ if cmd_str.eq_ignore_ascii_case("lpop") => {
675 if !(2..=3).contains(&count) {
676 return Err(ParseError::WrongArity(
677 "LPOP requires 1 or 2 arguments".to_string(),
678 ));
679 }
680 let key = cursor.read_bulk_string()?;
681 let count_opt = if count == 3 {
682 let count_bytes = cursor.read_bulk_string()?;
683 let count_str = std::str::from_utf8(count_bytes)
684 .map_err(|_| ParseError::Protocol("invalid UTF-8 in count".to_string()))?;
685 Some(
686 count_str
687 .parse::<usize>()
688 .map_err(|_| ParseError::Protocol("invalid count value".to_string()))?,
689 )
690 } else {
691 None
692 };
693 Command::LPop {
694 key,
695 count: count_opt,
696 }
697 }
698
699 _ if cmd_str.eq_ignore_ascii_case("rpop") => {
700 if !(2..=3).contains(&count) {
701 return Err(ParseError::WrongArity(
702 "RPOP requires 1 or 2 arguments".to_string(),
703 ));
704 }
705 let key = cursor.read_bulk_string()?;
706 let count_opt = if count == 3 {
707 let count_bytes = cursor.read_bulk_string()?;
708 let count_str = std::str::from_utf8(count_bytes)
709 .map_err(|_| ParseError::Protocol("invalid UTF-8 in count".to_string()))?;
710 Some(
711 count_str
712 .parse::<usize>()
713 .map_err(|_| ParseError::Protocol("invalid count value".to_string()))?,
714 )
715 } else {
716 None
717 };
718 Command::RPop {
719 key,
720 count: count_opt,
721 }
722 }
723
724 _ if cmd_str.eq_ignore_ascii_case("lrange") => {
725 if count != 4 {
726 return Err(ParseError::WrongArity(
727 "LRANGE requires exactly 3 arguments".to_string(),
728 ));
729 }
730 let key = cursor.read_bulk_string()?;
731 let start_bytes = cursor.read_bulk_string()?;
732 let stop_bytes = cursor.read_bulk_string()?;
733 let start_str = std::str::from_utf8(start_bytes)
734 .map_err(|_| ParseError::Protocol("invalid UTF-8 in start".to_string()))?;
735 let stop_str = std::str::from_utf8(stop_bytes)
736 .map_err(|_| ParseError::Protocol("invalid UTF-8 in stop".to_string()))?;
737 let start = start_str
738 .parse::<i64>()
739 .map_err(|_| ParseError::Protocol("invalid start value".to_string()))?;
740 let stop = stop_str
741 .parse::<i64>()
742 .map_err(|_| ParseError::Protocol("invalid stop value".to_string()))?;
743 Command::LRange { key, start, stop }
744 }
745
746 _ if cmd_str.eq_ignore_ascii_case("llen") => {
747 if count != 2 {
748 return Err(ParseError::WrongArity(
749 "LLEN requires exactly 1 argument".to_string(),
750 ));
751 }
752 let key = cursor.read_bulk_string()?;
753 Command::LLen { key }
754 }
755
756 _ if cmd_str.eq_ignore_ascii_case("lindex") => {
757 if count != 3 {
758 return Err(ParseError::WrongArity(
759 "LINDEX requires exactly 2 arguments".to_string(),
760 ));
761 }
762 let key = cursor.read_bulk_string()?;
763 let index_bytes = cursor.read_bulk_string()?;
764 let index_str = std::str::from_utf8(index_bytes)
765 .map_err(|_| ParseError::Protocol("invalid UTF-8 in index".to_string()))?;
766 let index = index_str
767 .parse::<i64>()
768 .map_err(|_| ParseError::Protocol("invalid index value".to_string()))?;
769 Command::LIndex { key, index }
770 }
771
772 _ if cmd_str.eq_ignore_ascii_case("lset") => {
773 if count != 4 {
774 return Err(ParseError::WrongArity(
775 "LSET requires exactly 3 arguments".to_string(),
776 ));
777 }
778 let key = cursor.read_bulk_string()?;
779 let index_bytes = cursor.read_bulk_string()?;
780 let value = cursor.read_bulk_string()?;
781 let index_str = std::str::from_utf8(index_bytes)
782 .map_err(|_| ParseError::Protocol("invalid UTF-8 in index".to_string()))?;
783 let index = index_str
784 .parse::<i64>()
785 .map_err(|_| ParseError::Protocol("invalid index value".to_string()))?;
786 Command::LSet { key, index, value }
787 }
788
789 _ if cmd_str.eq_ignore_ascii_case("ltrim") => {
790 if count != 4 {
791 return Err(ParseError::WrongArity(
792 "LTRIM requires exactly 3 arguments".to_string(),
793 ));
794 }
795 let key = cursor.read_bulk_string()?;
796 let start_bytes = cursor.read_bulk_string()?;
797 let stop_bytes = cursor.read_bulk_string()?;
798 let start_str = std::str::from_utf8(start_bytes)
799 .map_err(|_| ParseError::Protocol("invalid UTF-8 in start".to_string()))?;
800 let stop_str = std::str::from_utf8(stop_bytes)
801 .map_err(|_| ParseError::Protocol("invalid UTF-8 in stop".to_string()))?;
802 let start = start_str
803 .parse::<i64>()
804 .map_err(|_| ParseError::Protocol("invalid start value".to_string()))?;
805 let stop = stop_str
806 .parse::<i64>()
807 .map_err(|_| ParseError::Protocol("invalid stop value".to_string()))?;
808 Command::LTrim { key, start, stop }
809 }
810
811 _ if cmd_str.eq_ignore_ascii_case("lpushx") => {
812 if count < 3 {
813 return Err(ParseError::WrongArity(
814 "LPUSHX requires at least 2 arguments".to_string(),
815 ));
816 }
817 let key = cursor.read_bulk_string()?;
818 let mut values = Vec::with_capacity(count - 2);
819 for _ in 0..(count - 2) {
820 values.push(cursor.read_bulk_string()?);
821 }
822 Command::LPushX { key, values }
823 }
824
825 _ if cmd_str.eq_ignore_ascii_case("rpushx") => {
826 if count < 3 {
827 return Err(ParseError::WrongArity(
828 "RPUSHX requires at least 2 arguments".to_string(),
829 ));
830 }
831 let key = cursor.read_bulk_string()?;
832 let mut values = Vec::with_capacity(count - 2);
833 for _ in 0..(count - 2) {
834 values.push(cursor.read_bulk_string()?);
835 }
836 Command::RPushX { key, values }
837 }
838
839 _ if cmd_str.eq_ignore_ascii_case("sadd") => {
843 if count < 3 {
844 return Err(ParseError::WrongArity(
845 "SADD requires at least 2 arguments".to_string(),
846 ));
847 }
848 let key = cursor.read_bulk_string()?;
849 let mut members = Vec::with_capacity(count - 2);
850 for _ in 0..(count - 2) {
851 members.push(cursor.read_bulk_string()?);
852 }
853 Command::SAdd { key, members }
854 }
855
856 _ if cmd_str.eq_ignore_ascii_case("srem") => {
857 if count < 3 {
858 return Err(ParseError::WrongArity(
859 "SREM requires at least 2 arguments".to_string(),
860 ));
861 }
862 let key = cursor.read_bulk_string()?;
863 let mut members = Vec::with_capacity(count - 2);
864 for _ in 0..(count - 2) {
865 members.push(cursor.read_bulk_string()?);
866 }
867 Command::SRem { key, members }
868 }
869
870 _ if cmd_str.eq_ignore_ascii_case("smembers") => {
871 if count != 2 {
872 return Err(ParseError::WrongArity(
873 "SMEMBERS requires exactly 1 argument".to_string(),
874 ));
875 }
876 let key = cursor.read_bulk_string()?;
877 Command::SMembers { key }
878 }
879
880 _ if cmd_str.eq_ignore_ascii_case("sismember") => {
881 if count != 3 {
882 return Err(ParseError::WrongArity(
883 "SISMEMBER requires exactly 2 arguments".to_string(),
884 ));
885 }
886 let key = cursor.read_bulk_string()?;
887 let member = cursor.read_bulk_string()?;
888 Command::SIsMember { key, member }
889 }
890
891 _ if cmd_str.eq_ignore_ascii_case("smismember") => {
892 if count < 3 {
893 return Err(ParseError::WrongArity(
894 "SMISMEMBER requires at least 2 arguments".to_string(),
895 ));
896 }
897 let key = cursor.read_bulk_string()?;
898 let mut members = Vec::with_capacity(count - 2);
899 for _ in 0..(count - 2) {
900 members.push(cursor.read_bulk_string()?);
901 }
902 Command::SMisMember { key, members }
903 }
904
905 _ if cmd_str.eq_ignore_ascii_case("scard") => {
906 if count != 2 {
907 return Err(ParseError::WrongArity(
908 "SCARD requires exactly 1 argument".to_string(),
909 ));
910 }
911 let key = cursor.read_bulk_string()?;
912 Command::SCard { key }
913 }
914
915 _ if cmd_str.eq_ignore_ascii_case("spop") => {
916 if !(2..=3).contains(&count) {
917 return Err(ParseError::WrongArity(
918 "SPOP requires 1 or 2 arguments".to_string(),
919 ));
920 }
921 let key = cursor.read_bulk_string()?;
922 let count_opt = if count == 3 {
923 let count_bytes = cursor.read_bulk_string()?;
924 let count_str = std::str::from_utf8(count_bytes)
925 .map_err(|_| ParseError::Protocol("invalid UTF-8 in count".to_string()))?;
926 Some(
927 count_str
928 .parse::<usize>()
929 .map_err(|_| ParseError::Protocol("invalid count value".to_string()))?,
930 )
931 } else {
932 None
933 };
934 Command::SPop {
935 key,
936 count: count_opt,
937 }
938 }
939
940 _ if cmd_str.eq_ignore_ascii_case("srandmember") => {
941 if !(2..=3).contains(&count) {
942 return Err(ParseError::WrongArity(
943 "SRANDMEMBER requires 1 or 2 arguments".to_string(),
944 ));
945 }
946 let key = cursor.read_bulk_string()?;
947 let count_opt = if count == 3 {
948 let count_bytes = cursor.read_bulk_string()?;
949 let count_str = std::str::from_utf8(count_bytes)
950 .map_err(|_| ParseError::Protocol("invalid UTF-8 in count".to_string()))?;
951 Some(
952 count_str
953 .parse::<i64>()
954 .map_err(|_| ParseError::Protocol("invalid count value".to_string()))?,
955 )
956 } else {
957 None
958 };
959 Command::SRandMember {
960 key,
961 count: count_opt,
962 }
963 }
964
965 _ if cmd_str.eq_ignore_ascii_case("type") => {
969 if count != 2 {
970 return Err(ParseError::WrongArity(
971 "TYPE requires exactly 1 argument".to_string(),
972 ));
973 }
974 let key = cursor.read_bulk_string()?;
975 Command::Type { key }
976 }
977
978 #[cfg(feature = "resp3")]
979 _ if cmd_str.eq_ignore_ascii_case("hello") => {
980 let mut proto_version = None;
981 let mut auth = None;
982 let mut client_name = None;
983
984 let mut remaining_args = count - 1;
985
986 if remaining_args > 0 {
988 let version_bytes = cursor.read_bulk_string()?;
989 let version_str = std::str::from_utf8(version_bytes).map_err(|_| {
990 ParseError::Protocol("invalid UTF-8 in version".to_string())
991 })?;
992 let version: u8 = version_str.parse().map_err(|_| {
993 ParseError::Protocol("invalid protocol version".to_string())
994 })?;
995 proto_version = Some(version);
996 remaining_args -= 1;
997 }
998
999 while remaining_args > 0 {
1001 let option = cursor.read_bulk_string()?;
1002 let option_str = std::str::from_utf8(option)
1003 .map_err(|_| ParseError::Protocol("invalid UTF-8 in option".to_string()))?;
1004
1005 if option_str.eq_ignore_ascii_case("auth") {
1006 if remaining_args < 3 {
1007 return Err(ParseError::Protocol(
1008 "AUTH requires username and password".to_string(),
1009 ));
1010 }
1011 let username = cursor.read_bulk_string()?;
1012 let password = cursor.read_bulk_string()?;
1013 auth = Some((username, password));
1014 remaining_args -= 3;
1015 } else if option_str.eq_ignore_ascii_case("setname") {
1016 if remaining_args < 2 {
1017 return Err(ParseError::Protocol(
1018 "SETNAME requires a name".to_string(),
1019 ));
1020 }
1021 let name = cursor.read_bulk_string()?;
1022 client_name = Some(name);
1023 remaining_args -= 2;
1024 } else {
1025 return Err(ParseError::Protocol(format!(
1026 "unknown HELLO option: {}",
1027 option_str
1028 )));
1029 }
1030 }
1031
1032 Command::Hello {
1033 proto_version,
1034 auth,
1035 client_name,
1036 }
1037 }
1038
1039 _ => {
1040 return Err(ParseError::UnknownCommand(cmd_str.to_string()));
1041 }
1042 };
1043
1044 Ok((command, cursor.position()))
1045 }
1046
1047 pub fn name(&self) -> &'static str {
1049 match self {
1050 Command::Ping => "PING",
1051 Command::Get { .. } => "GET",
1052 Command::Set { .. } => "SET",
1053 Command::Del { .. } => "DEL",
1054 Command::MGet { .. } => "MGET",
1055 Command::Config { .. } => "CONFIG",
1056 Command::Cluster { .. } => "CLUSTER",
1057 Command::Asking => "ASKING",
1058 Command::ReadOnly => "READONLY",
1059 Command::ReadWrite => "READWRITE",
1060 Command::FlushDb => "FLUSHDB",
1061 Command::FlushAll => "FLUSHALL",
1062 Command::Incr { .. } => "INCR",
1063 Command::Decr { .. } => "DECR",
1064 Command::IncrBy { .. } => "INCRBY",
1065 Command::DecrBy { .. } => "DECRBY",
1066 Command::Append { .. } => "APPEND",
1067 Command::HSet { .. } => "HSET",
1069 Command::HGet { .. } => "HGET",
1070 Command::HMGet { .. } => "HMGET",
1071 Command::HGetAll { .. } => "HGETALL",
1072 Command::HDel { .. } => "HDEL",
1073 Command::HExists { .. } => "HEXISTS",
1074 Command::HLen { .. } => "HLEN",
1075 Command::HKeys { .. } => "HKEYS",
1076 Command::HVals { .. } => "HVALS",
1077 Command::HSetNx { .. } => "HSETNX",
1078 Command::HIncrBy { .. } => "HINCRBY",
1079 Command::LPush { .. } => "LPUSH",
1081 Command::RPush { .. } => "RPUSH",
1082 Command::LPop { .. } => "LPOP",
1083 Command::RPop { .. } => "RPOP",
1084 Command::LRange { .. } => "LRANGE",
1085 Command::LLen { .. } => "LLEN",
1086 Command::LIndex { .. } => "LINDEX",
1087 Command::LSet { .. } => "LSET",
1088 Command::LTrim { .. } => "LTRIM",
1089 Command::LPushX { .. } => "LPUSHX",
1090 Command::RPushX { .. } => "RPUSHX",
1091 Command::SAdd { .. } => "SADD",
1093 Command::SRem { .. } => "SREM",
1094 Command::SMembers { .. } => "SMEMBERS",
1095 Command::SIsMember { .. } => "SISMEMBER",
1096 Command::SMisMember { .. } => "SMISMEMBER",
1097 Command::SCard { .. } => "SCARD",
1098 Command::SPop { .. } => "SPOP",
1099 Command::SRandMember { .. } => "SRANDMEMBER",
1100 Command::Type { .. } => "TYPE",
1102 #[cfg(feature = "resp3")]
1103 Command::Hello { .. } => "HELLO",
1104 }
1105 }
1106}
1107
1108struct Cursor<'a> {
1110 buffer: &'a [u8],
1111 pos: usize,
1112 max_bulk_string_len: usize,
1113}
1114
1115impl<'a> Cursor<'a> {
1116 fn new(buffer: &'a [u8], max_bulk_string_len: usize) -> Self {
1117 Self {
1118 buffer,
1119 pos: 0,
1120 max_bulk_string_len,
1121 }
1122 }
1123
1124 #[inline]
1125 fn remaining(&self) -> usize {
1126 self.buffer.len() - self.pos
1127 }
1128
1129 #[inline]
1130 fn position(&self) -> usize {
1131 self.pos
1132 }
1133
1134 #[inline]
1135 fn get_u8(&mut self) -> u8 {
1136 let b = self.buffer[self.pos];
1137 self.pos += 1;
1138 b
1139 }
1140
1141 fn read_integer(&mut self) -> Result<usize, ParseError> {
1142 let line = self.read_line()?;
1143
1144 if line.is_empty() {
1145 return Err(ParseError::InvalidInteger("empty integer".to_string()));
1146 }
1147
1148 if line.len() > 19 {
1151 return Err(ParseError::InvalidInteger("integer too large".to_string()));
1152 }
1153
1154 let mut result = 0usize;
1155 for &byte in line {
1156 if !byte.is_ascii_digit() {
1157 return Err(ParseError::InvalidInteger(
1158 "non-digit character".to_string(),
1159 ));
1160 }
1161 result = result
1162 .checked_mul(10)
1163 .and_then(|r| r.checked_add((byte - b'0') as usize))
1164 .ok_or_else(|| ParseError::InvalidInteger("integer overflow".to_string()))?;
1165 }
1166 Ok(result)
1167 }
1168
1169 fn read_bulk_string(&mut self) -> Result<&'a [u8], ParseError> {
1170 if self.remaining() < 1 {
1171 return Err(ParseError::Incomplete);
1172 }
1173
1174 if self.get_u8() != b'$' {
1175 return Err(ParseError::Protocol("expected bulk string".to_string()));
1176 }
1177
1178 let len = self.read_integer()?;
1179
1180 if len > self.max_bulk_string_len {
1182 return Err(ParseError::BulkStringTooLong {
1183 len,
1184 max: self.max_bulk_string_len,
1185 });
1186 }
1187
1188 if self.remaining() < len + 2 {
1189 return Err(ParseError::Incomplete);
1190 }
1191
1192 let data = &self.buffer[self.pos..self.pos + len];
1193 self.pos += len;
1194
1195 if self.remaining() < 2 {
1197 return Err(ParseError::Incomplete);
1198 }
1199 if self.get_u8() != b'\r' || self.get_u8() != b'\n' {
1200 return Err(ParseError::Protocol(
1201 "expected CRLF after bulk string".to_string(),
1202 ));
1203 }
1204
1205 Ok(data)
1206 }
1207
1208 fn read_line(&mut self) -> Result<&'a [u8], ParseError> {
1209 let start = self.pos;
1210 let slice = &self.buffer[start..];
1211
1212 if let Some(pos) = memchr::memchr(b'\r', slice)
1213 && pos + 1 < slice.len()
1214 && slice[pos + 1] == b'\n'
1215 {
1216 let end = start + pos;
1217 let line = &self.buffer[start..end];
1218 self.pos = end + 2;
1219 return Ok(line);
1220 }
1221
1222 Err(ParseError::Incomplete)
1223 }
1224}
1225
1226#[cfg(test)]
1227mod tests {
1228 use super::*;
1229
1230 #[test]
1231 fn test_parse_ping() {
1232 let data = b"*1\r\n$4\r\nPING\r\n";
1233 let (cmd, consumed) = Command::parse(data).unwrap();
1234 assert_eq!(cmd, Command::Ping);
1235 assert_eq!(consumed, data.len());
1236 }
1237
1238 #[test]
1239 fn test_parse_get() {
1240 let data = b"*2\r\n$3\r\nGET\r\n$5\r\nmykey\r\n";
1241 let (cmd, consumed) = Command::parse(data).unwrap();
1242 assert_eq!(cmd, Command::Get { key: b"mykey" });
1243 assert_eq!(consumed, data.len());
1244 }
1245
1246 #[test]
1247 fn test_parse_set() {
1248 let data = b"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n";
1249 let (cmd, consumed) = Command::parse(data).unwrap();
1250 assert_eq!(
1251 cmd,
1252 Command::Set {
1253 key: b"mykey",
1254 value: b"myvalue",
1255 ex: None,
1256 px: None,
1257 nx: false,
1258 xx: false,
1259 }
1260 );
1261 assert_eq!(consumed, data.len());
1262 }
1263
1264 #[test]
1265 fn test_parse_set_ex() {
1266 let data = b"*5\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n$2\r\nEX\r\n$4\r\n3600\r\n";
1267 let (cmd, consumed) = Command::parse(data).unwrap();
1268 assert_eq!(
1269 cmd,
1270 Command::Set {
1271 key: b"mykey",
1272 value: b"myvalue",
1273 ex: Some(3600),
1274 px: None,
1275 nx: false,
1276 xx: false,
1277 }
1278 );
1279 assert_eq!(consumed, data.len());
1280 }
1281
1282 #[test]
1283 fn test_parse_set_px_nx() {
1284 let data =
1285 b"*5\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n$2\r\nPX\r\n$4\r\n1000\r\n*5\r\n$3\r\nSET\r\n$1\r\na\r\n$1\r\nb\r\n$2\r\nNX\r\n";
1286 let (cmd, _) = Command::parse(data).unwrap();
1287 if let Command::Set { px, .. } = cmd {
1288 assert_eq!(px, Some(1000));
1289 } else {
1290 panic!("Expected SET command");
1291 }
1292 }
1293
1294 #[test]
1295 fn test_parse_del() {
1296 let data = b"*2\r\n$3\r\nDEL\r\n$5\r\nmykey\r\n";
1297 let (cmd, consumed) = Command::parse(data).unwrap();
1298 assert_eq!(cmd, Command::Del { key: b"mykey" });
1299 assert_eq!(consumed, data.len());
1300 }
1301
1302 #[test]
1303 fn test_parse_mget() {
1304 let data = b"*4\r\n$4\r\nMGET\r\n$4\r\nkey1\r\n$4\r\nkey2\r\n$4\r\nkey3\r\n";
1305 let (cmd, consumed) = Command::parse(data).unwrap();
1306 assert_eq!(
1307 cmd,
1308 Command::MGet {
1309 keys: vec![b"key1" as &[u8], b"key2", b"key3"]
1310 }
1311 );
1312 assert_eq!(consumed, data.len());
1313 }
1314
1315 #[test]
1316 fn test_parse_config() {
1317 let data = b"*3\r\n$6\r\nCONFIG\r\n$3\r\nGET\r\n$10\r\nmaxclients\r\n";
1318 let (cmd, consumed) = Command::parse(data).unwrap();
1319 assert_eq!(
1320 cmd,
1321 Command::Config {
1322 subcommand: b"GET",
1323 args: vec![b"maxclients" as &[u8]]
1324 }
1325 );
1326 assert_eq!(consumed, data.len());
1327 }
1328
1329 #[test]
1330 fn test_parse_flushdb() {
1331 let data = b"*1\r\n$7\r\nFLUSHDB\r\n";
1332 let (cmd, consumed) = Command::parse(data).unwrap();
1333 assert_eq!(cmd, Command::FlushDb);
1334 assert_eq!(consumed, data.len());
1335 }
1336
1337 #[test]
1338 fn test_parse_flushall() {
1339 let data = b"*1\r\n$8\r\nFLUSHALL\r\n";
1340 let (cmd, consumed) = Command::parse(data).unwrap();
1341 assert_eq!(cmd, Command::FlushAll);
1342 assert_eq!(consumed, data.len());
1343 }
1344
1345 #[test]
1346 fn test_parse_case_insensitive() {
1347 let data = b"*2\r\n$3\r\nget\r\n$5\r\nmykey\r\n";
1348 let (cmd, _) = Command::parse(data).unwrap();
1349 assert_eq!(cmd, Command::Get { key: b"mykey" });
1350
1351 let data = b"*2\r\n$3\r\nGeT\r\n$5\r\nmykey\r\n";
1352 let (cmd, _) = Command::parse(data).unwrap();
1353 assert_eq!(cmd, Command::Get { key: b"mykey" });
1354 }
1355
1356 #[test]
1357 fn test_parse_incomplete() {
1358 assert!(matches!(
1359 Command::parse(b"*2\r\n$3\r\nGET"),
1360 Err(ParseError::Incomplete)
1361 ));
1362 assert!(matches!(
1363 Command::parse(b"*2\r\n"),
1364 Err(ParseError::Incomplete)
1365 ));
1366 assert!(matches!(Command::parse(b""), Err(ParseError::Incomplete)));
1367 }
1368
1369 #[test]
1370 fn test_parse_unknown_command() {
1371 let data = b"*1\r\n$7\r\nUNKNOWN\r\n";
1372 assert!(matches!(
1373 Command::parse(data),
1374 Err(ParseError::UnknownCommand(_))
1375 ));
1376 }
1377
1378 #[test]
1379 fn test_parse_wrong_arity() {
1380 let data = b"*1\r\n$3\r\nGET\r\n"; assert!(matches!(
1382 Command::parse(data),
1383 Err(ParseError::WrongArity(_))
1384 ));
1385 }
1386
1387 #[test]
1388 fn test_command_name() {
1389 assert_eq!(Command::Ping.name(), "PING");
1390 assert_eq!(Command::Get { key: b"k" }.name(), "GET");
1391 assert_eq!(
1392 Command::Set {
1393 key: b"k",
1394 value: b"v",
1395 ex: None,
1396 px: None,
1397 nx: false,
1398 xx: false
1399 }
1400 .name(),
1401 "SET"
1402 );
1403 assert_eq!(Command::Del { key: b"k" }.name(), "DEL");
1404 assert_eq!(Command::MGet { keys: vec![] }.name(), "MGET");
1405 assert_eq!(
1406 Command::Config {
1407 subcommand: b"GET",
1408 args: vec![]
1409 }
1410 .name(),
1411 "CONFIG"
1412 );
1413 assert_eq!(Command::FlushDb.name(), "FLUSHDB");
1414 assert_eq!(Command::FlushAll.name(), "FLUSHALL");
1415 }
1416
1417 #[test]
1418 fn test_parse_set_xx() {
1419 let data = b"*4\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n$2\r\nXX\r\n";
1420 let (cmd, consumed) = Command::parse(data).unwrap();
1421 if let Command::Set { xx, nx, .. } = cmd {
1422 assert!(xx);
1423 assert!(!nx);
1424 } else {
1425 panic!("Expected SET command");
1426 }
1427 assert_eq!(consumed, data.len());
1428 }
1429
1430 #[test]
1431 fn test_parse_set_unknown_option() {
1432 let data = b"*4\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n$7\r\nINVALID\r\n";
1433 let result = Command::parse(data);
1434 assert!(matches!(result, Err(ParseError::Protocol(_))));
1435 }
1436
1437 #[test]
1438 fn test_parse_set_ex_missing_value() {
1439 let data = b"*4\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n$2\r\nEX\r\n";
1441 let result = Command::parse(data);
1442 assert!(matches!(result, Err(ParseError::Protocol(_))));
1443 }
1444
1445 #[test]
1446 fn test_parse_set_px_missing_value() {
1447 let data = b"*4\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n$2\r\nPX\r\n";
1449 let result = Command::parse(data);
1450 assert!(matches!(result, Err(ParseError::Protocol(_))));
1451 }
1452
1453 #[test]
1454 fn test_parse_set_invalid_ttl() {
1455 let data = b"*5\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n$2\r\nEX\r\n$3\r\nabc\r\n";
1457 let result = Command::parse(data);
1458 assert!(matches!(result, Err(ParseError::Protocol(_))));
1459 }
1460
1461 #[test]
1462 fn test_parse_ping_wrong_arity() {
1463 let data = b"*2\r\n$4\r\nPING\r\n$5\r\nextra\r\n";
1465 let result = Command::parse(data);
1466 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1467 }
1468
1469 #[test]
1470 fn test_parse_del_wrong_arity() {
1471 let data = b"*1\r\n$3\r\nDEL\r\n";
1473 let result = Command::parse(data);
1474 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1475 }
1476
1477 #[test]
1478 fn test_parse_mget_wrong_arity() {
1479 let data = b"*1\r\n$4\r\nMGET\r\n";
1481 let result = Command::parse(data);
1482 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1483 }
1484
1485 #[test]
1486 fn test_parse_config_wrong_arity() {
1487 let data = b"*1\r\n$6\r\nCONFIG\r\n";
1489 let result = Command::parse(data);
1490 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1491 }
1492
1493 #[test]
1494 fn test_parse_set_wrong_arity() {
1495 let data = b"*2\r\n$3\r\nSET\r\n$1\r\nk\r\n";
1497 let result = Command::parse(data);
1498 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1499 }
1500
1501 #[test]
1502 fn test_parse_not_array() {
1503 let data = b"+OK\r\n";
1505 let result = Command::parse(data);
1506 assert!(matches!(result, Err(ParseError::Protocol(_))));
1507 }
1508
1509 #[test]
1510 fn test_parse_empty_array() {
1511 let data = b"*0\r\n";
1513 let result = Command::parse(data);
1514 assert!(matches!(result, Err(ParseError::Protocol(_))));
1515 }
1516
1517 #[test]
1518 fn test_command_debug() {
1519 let cmd = Command::Ping;
1520 let debug_str = format!("{:?}", cmd);
1521 assert!(debug_str.contains("Ping"));
1522 }
1523
1524 #[test]
1525 fn test_command_clone() {
1526 let cmd1 = Command::Get { key: b"mykey" };
1527 let cmd2 = cmd1.clone();
1528 assert_eq!(cmd1, cmd2);
1529 }
1530
1531 #[test]
1532 fn test_command_eq() {
1533 assert_eq!(Command::Ping, Command::Ping);
1534 assert_ne!(Command::Ping, Command::FlushDb);
1535 assert_eq!(Command::Get { key: b"a" }, Command::Get { key: b"a" });
1536 assert_ne!(Command::Get { key: b"a" }, Command::Get { key: b"b" });
1537 }
1538
1539 #[test]
1540 fn test_parse_config_set() {
1541 let data = b"*4\r\n$6\r\nCONFIG\r\n$3\r\nSET\r\n$10\r\nmaxclients\r\n$3\r\n100\r\n";
1542 let (cmd, consumed) = Command::parse(data).unwrap();
1543 if let Command::Config { subcommand, args } = cmd {
1544 assert_eq!(subcommand, b"SET");
1545 assert_eq!(args.len(), 2);
1546 } else {
1547 panic!("Expected CONFIG command");
1548 }
1549 assert_eq!(consumed, data.len());
1550 }
1551
1552 #[test]
1553 fn test_parse_config_no_args() {
1554 let data = b"*2\r\n$6\r\nCONFIG\r\n$4\r\nINFO\r\n";
1555 let (cmd, consumed) = Command::parse(data).unwrap();
1556 if let Command::Config { subcommand, args } = cmd {
1557 assert_eq!(subcommand, b"INFO");
1558 assert!(args.is_empty());
1559 } else {
1560 panic!("Expected CONFIG command");
1561 }
1562 assert_eq!(consumed, data.len());
1563 }
1564
1565 #[test]
1566 fn test_parse_integer_too_large() {
1567 let data = b"*12345678901234567890123\r\n$4\r\nPING\r\n";
1569 let result = Command::parse(data);
1570 assert!(matches!(result, Err(ParseError::InvalidInteger(_))));
1571 }
1572
1573 #[test]
1574 fn test_parse_integer_non_digit() {
1575 let data = b"*12a\r\n$4\r\nPING\r\n";
1577 let result = Command::parse(data);
1578 assert!(matches!(result, Err(ParseError::InvalidInteger(_))));
1579 }
1580
1581 #[test]
1582 fn test_parse_integer_overflow() {
1583 let data = b"*99999999999999999999\r\n$4\r\nPING\r\n";
1585 let result = Command::parse(data);
1586 assert!(matches!(result, Err(ParseError::InvalidInteger(_))));
1587 }
1588
1589 #[test]
1590 fn test_parse_bulk_string_missing_crlf() {
1591 let data = b"*2\r\n$3\r\nGET\r\n$5\r\nmykeyXX";
1593 let result = Command::parse(data);
1594 assert!(matches!(result, Err(ParseError::Protocol(_))));
1595 }
1596
1597 #[test]
1598 fn test_parse_not_bulk_string() {
1599 let data = b"*2\r\n+GET\r\n$5\r\nmykey\r\n";
1601 let result = Command::parse(data);
1602 assert!(matches!(result, Err(ParseError::Protocol(_))));
1603 }
1604
1605 #[test]
1606 fn test_parse_invalid_utf8_command() {
1607 let data = b"*1\r\n$2\r\n\xff\xfe\r\n";
1609 let result = Command::parse(data);
1610 assert!(matches!(result, Err(ParseError::Protocol(_))));
1611 }
1612
1613 #[test]
1614 fn test_parse_set_invalid_utf8_option() {
1615 let data = b"*4\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n$2\r\n\xff\xfe\r\n";
1617 let result = Command::parse(data);
1618 assert!(matches!(result, Err(ParseError::Protocol(_))));
1619 }
1620
1621 #[test]
1622 fn test_parse_set_invalid_utf8_in_ttl() {
1623 let data = b"*5\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n$2\r\nEX\r\n$2\r\n\xff\xfe\r\n";
1625 let result = Command::parse(data);
1626 assert!(matches!(result, Err(ParseError::Protocol(_))));
1627 }
1628
1629 #[test]
1630 fn test_parse_set_invalid_px_ttl() {
1631 let data = b"*5\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n$2\r\nPX\r\n$3\r\nabc\r\n";
1633 let result = Command::parse(data);
1634 assert!(matches!(result, Err(ParseError::Protocol(_))));
1635 }
1636
1637 #[test]
1638 fn test_parse_set_nx() {
1639 let data = b"*4\r\n$3\r\nSET\r\n$1\r\nk\r\n$1\r\nv\r\n$2\r\nNX\r\n";
1640 let (cmd, consumed) = Command::parse(data).unwrap();
1641 if let Command::Set { nx, xx, .. } = cmd {
1642 assert!(nx);
1643 assert!(!xx);
1644 } else {
1645 panic!("Expected SET command");
1646 }
1647 assert_eq!(consumed, data.len());
1648 }
1649
1650 #[test]
1651 fn test_parse_bulk_string_incomplete() {
1652 let data = b"*2\r\n$3\r\nGET\r\n$100\r\nmykey\r\n"; let result = Command::parse(data);
1655 assert!(matches!(result, Err(ParseError::Incomplete)));
1656 }
1657
1658 #[test]
1659 fn test_parse_mget_huge_count_no_oom() {
1660 let data = b"*1177777777\r\n$4\r\nmGet\r\n";
1662 let result = Command::parse(data);
1663 assert!(matches!(result, Err(ParseError::Protocol(_))));
1664 }
1665
1666 #[test]
1667 fn test_parse_array_too_large() {
1668 let data = b"*1048577\r\n$4\r\nPING\r\n"; let result = Command::parse(data);
1671 assert!(matches!(result, Err(ParseError::Protocol(_))));
1672 }
1673
1674 #[test]
1679 fn test_parse_incr() {
1680 let data = b"*2\r\n$4\r\nINCR\r\n$7\r\ncounter\r\n";
1681 let (cmd, consumed) = Command::parse(data).unwrap();
1682 assert_eq!(cmd, Command::Incr { key: b"counter" });
1683 assert_eq!(consumed, data.len());
1684 }
1685
1686 #[test]
1687 fn test_parse_incr_case_insensitive() {
1688 let data = b"*2\r\n$4\r\nincr\r\n$3\r\nkey\r\n";
1689 let (cmd, _) = Command::parse(data).unwrap();
1690 assert_eq!(cmd, Command::Incr { key: b"key" });
1691 }
1692
1693 #[test]
1694 fn test_parse_incr_wrong_arity_no_key() {
1695 let data = b"*1\r\n$4\r\nINCR\r\n";
1696 let result = Command::parse(data);
1697 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1698 }
1699
1700 #[test]
1701 fn test_parse_incr_wrong_arity_extra_args() {
1702 let data = b"*3\r\n$4\r\nINCR\r\n$3\r\nkey\r\n$5\r\nextra\r\n";
1703 let result = Command::parse(data);
1704 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1705 }
1706
1707 #[test]
1708 fn test_parse_decr() {
1709 let data = b"*2\r\n$4\r\nDECR\r\n$7\r\ncounter\r\n";
1710 let (cmd, consumed) = Command::parse(data).unwrap();
1711 assert_eq!(cmd, Command::Decr { key: b"counter" });
1712 assert_eq!(consumed, data.len());
1713 }
1714
1715 #[test]
1716 fn test_parse_decr_case_insensitive() {
1717 let data = b"*2\r\n$4\r\ndecr\r\n$3\r\nkey\r\n";
1718 let (cmd, _) = Command::parse(data).unwrap();
1719 assert_eq!(cmd, Command::Decr { key: b"key" });
1720 }
1721
1722 #[test]
1723 fn test_parse_decr_wrong_arity() {
1724 let data = b"*1\r\n$4\r\nDECR\r\n";
1725 let result = Command::parse(data);
1726 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1727 }
1728
1729 #[test]
1730 fn test_parse_incrby() {
1731 let data = b"*3\r\n$6\r\nINCRBY\r\n$7\r\ncounter\r\n$2\r\n10\r\n";
1732 let (cmd, consumed) = Command::parse(data).unwrap();
1733 assert_eq!(
1734 cmd,
1735 Command::IncrBy {
1736 key: b"counter",
1737 delta: 10
1738 }
1739 );
1740 assert_eq!(consumed, data.len());
1741 }
1742
1743 #[test]
1744 fn test_parse_incrby_negative() {
1745 let data = b"*3\r\n$6\r\nINCRBY\r\n$3\r\nkey\r\n$3\r\n-10\r\n";
1746 let (cmd, consumed) = Command::parse(data).unwrap();
1747 assert_eq!(
1748 cmd,
1749 Command::IncrBy {
1750 key: b"key",
1751 delta: -10
1752 }
1753 );
1754 assert_eq!(consumed, data.len());
1755 }
1756
1757 #[test]
1758 fn test_parse_incrby_case_insensitive() {
1759 let data = b"*3\r\n$6\r\nincrby\r\n$3\r\nkey\r\n$1\r\n5\r\n";
1760 let (cmd, _) = Command::parse(data).unwrap();
1761 assert_eq!(
1762 cmd,
1763 Command::IncrBy {
1764 key: b"key",
1765 delta: 5
1766 }
1767 );
1768 }
1769
1770 #[test]
1771 fn test_parse_incrby_wrong_arity() {
1772 let data = b"*2\r\n$6\r\nINCRBY\r\n$3\r\nkey\r\n";
1773 let result = Command::parse(data);
1774 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1775 }
1776
1777 #[test]
1778 fn test_parse_incrby_invalid_delta() {
1779 let data = b"*3\r\n$6\r\nINCRBY\r\n$3\r\nkey\r\n$3\r\nabc\r\n";
1780 let result = Command::parse(data);
1781 assert!(matches!(result, Err(ParseError::Protocol(_))));
1782 }
1783
1784 #[test]
1785 fn test_parse_decrby() {
1786 let data = b"*3\r\n$6\r\nDECRBY\r\n$7\r\ncounter\r\n$2\r\n10\r\n";
1787 let (cmd, consumed) = Command::parse(data).unwrap();
1788 assert_eq!(
1789 cmd,
1790 Command::DecrBy {
1791 key: b"counter",
1792 delta: 10
1793 }
1794 );
1795 assert_eq!(consumed, data.len());
1796 }
1797
1798 #[test]
1799 fn test_parse_decrby_case_insensitive() {
1800 let data = b"*3\r\n$6\r\ndecrby\r\n$3\r\nkey\r\n$1\r\n5\r\n";
1801 let (cmd, _) = Command::parse(data).unwrap();
1802 assert_eq!(
1803 cmd,
1804 Command::DecrBy {
1805 key: b"key",
1806 delta: 5
1807 }
1808 );
1809 }
1810
1811 #[test]
1812 fn test_parse_decrby_wrong_arity() {
1813 let data = b"*2\r\n$6\r\nDECRBY\r\n$3\r\nkey\r\n";
1814 let result = Command::parse(data);
1815 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1816 }
1817
1818 #[test]
1819 fn test_parse_decrby_invalid_delta() {
1820 let data = b"*3\r\n$6\r\nDECRBY\r\n$3\r\nkey\r\n$3\r\nabc\r\n";
1821 let result = Command::parse(data);
1822 assert!(matches!(result, Err(ParseError::Protocol(_))));
1823 }
1824
1825 #[test]
1826 fn test_incr_decr_command_names() {
1827 assert_eq!(Command::Incr { key: b"k" }.name(), "INCR");
1828 assert_eq!(Command::Decr { key: b"k" }.name(), "DECR");
1829 assert_eq!(
1830 Command::IncrBy {
1831 key: b"k",
1832 delta: 1
1833 }
1834 .name(),
1835 "INCRBY"
1836 );
1837 assert_eq!(
1838 Command::DecrBy {
1839 key: b"k",
1840 delta: 1
1841 }
1842 .name(),
1843 "DECRBY"
1844 );
1845 }
1846
1847 #[test]
1852 fn test_parse_cluster_slots() {
1853 let data = b"*2\r\n$7\r\nCLUSTER\r\n$5\r\nSLOTS\r\n";
1854 let (cmd, consumed) = Command::parse(data).unwrap();
1855 assert_eq!(
1856 cmd,
1857 Command::Cluster {
1858 subcommand: b"SLOTS",
1859 args: vec![],
1860 }
1861 );
1862 assert_eq!(consumed, data.len());
1863 }
1864
1865 #[test]
1866 fn test_parse_cluster_info() {
1867 let data = b"*2\r\n$7\r\ncluster\r\n$4\r\nINFO\r\n";
1868 let (cmd, _) = Command::parse(data).unwrap();
1869 if let Command::Cluster { subcommand, args } = cmd {
1870 assert_eq!(subcommand, b"INFO");
1871 assert!(args.is_empty());
1872 } else {
1873 panic!("Expected CLUSTER command");
1874 }
1875 }
1876
1877 #[test]
1878 fn test_parse_cluster_wrong_arity() {
1879 let data = b"*1\r\n$7\r\nCLUSTER\r\n";
1880 let result = Command::parse(data);
1881 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1882 }
1883
1884 #[test]
1885 fn test_parse_asking() {
1886 let data = b"*1\r\n$6\r\nASKING\r\n";
1887 let (cmd, consumed) = Command::parse(data).unwrap();
1888 assert_eq!(cmd, Command::Asking);
1889 assert_eq!(consumed, data.len());
1890 }
1891
1892 #[test]
1893 fn test_parse_asking_case_insensitive() {
1894 let data = b"*1\r\n$6\r\nasking\r\n";
1895 let (cmd, _) = Command::parse(data).unwrap();
1896 assert_eq!(cmd, Command::Asking);
1897 }
1898
1899 #[test]
1900 fn test_parse_asking_wrong_arity() {
1901 let data = b"*2\r\n$6\r\nASKING\r\n$3\r\nfoo\r\n";
1902 let result = Command::parse(data);
1903 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1904 }
1905
1906 #[test]
1907 fn test_parse_readonly() {
1908 let data = b"*1\r\n$8\r\nREADONLY\r\n";
1909 let (cmd, consumed) = Command::parse(data).unwrap();
1910 assert_eq!(cmd, Command::ReadOnly);
1911 assert_eq!(consumed, data.len());
1912 }
1913
1914 #[test]
1915 fn test_parse_readonly_wrong_arity() {
1916 let data = b"*2\r\n$8\r\nREADONLY\r\n$3\r\nfoo\r\n";
1917 let result = Command::parse(data);
1918 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1919 }
1920
1921 #[test]
1922 fn test_parse_readwrite() {
1923 let data = b"*1\r\n$9\r\nREADWRITE\r\n";
1924 let (cmd, consumed) = Command::parse(data).unwrap();
1925 assert_eq!(cmd, Command::ReadWrite);
1926 assert_eq!(consumed, data.len());
1927 }
1928
1929 #[test]
1930 fn test_parse_readwrite_wrong_arity() {
1931 let data = b"*2\r\n$9\r\nREADWRITE\r\n$3\r\nfoo\r\n";
1932 let result = Command::parse(data);
1933 assert!(matches!(result, Err(ParseError::WrongArity(_))));
1934 }
1935
1936 #[test]
1937 fn test_cluster_command_names() {
1938 assert_eq!(
1939 Command::Cluster {
1940 subcommand: b"SLOTS",
1941 args: vec![]
1942 }
1943 .name(),
1944 "CLUSTER"
1945 );
1946 assert_eq!(Command::Asking.name(), "ASKING");
1947 assert_eq!(Command::ReadOnly.name(), "READONLY");
1948 assert_eq!(Command::ReadWrite.name(), "READWRITE");
1949 }
1950
1951 #[cfg(feature = "resp3")]
1956 mod resp3_tests {
1957 use super::*;
1958
1959 #[test]
1960 fn test_parse_hello_no_args() {
1961 let data = b"*1\r\n$5\r\nHELLO\r\n";
1962 let (cmd, consumed) = Command::parse(data).unwrap();
1963 assert_eq!(
1964 cmd,
1965 Command::Hello {
1966 proto_version: None,
1967 auth: None,
1968 client_name: None,
1969 }
1970 );
1971 assert_eq!(consumed, data.len());
1972 }
1973
1974 #[test]
1975 fn test_parse_hello_with_version() {
1976 let data = b"*2\r\n$5\r\nHELLO\r\n$1\r\n3\r\n";
1977 let (cmd, consumed) = Command::parse(data).unwrap();
1978 assert_eq!(
1979 cmd,
1980 Command::Hello {
1981 proto_version: Some(3),
1982 auth: None,
1983 client_name: None,
1984 }
1985 );
1986 assert_eq!(consumed, data.len());
1987 }
1988
1989 #[test]
1990 fn test_parse_hello_with_auth() {
1991 let data =
1993 b"*5\r\n$5\r\nHELLO\r\n$1\r\n3\r\n$4\r\nAUTH\r\n$4\r\nuser\r\n$4\r\npass\r\n";
1994 let (cmd, consumed) = Command::parse(data).unwrap();
1995 assert_eq!(
1996 cmd,
1997 Command::Hello {
1998 proto_version: Some(3),
1999 auth: Some((b"user" as &[u8], b"pass" as &[u8])),
2000 client_name: None,
2001 }
2002 );
2003 assert_eq!(consumed, data.len());
2004 }
2005
2006 #[test]
2007 fn test_parse_hello_with_setname() {
2008 let data = b"*4\r\n$5\r\nHELLO\r\n$1\r\n3\r\n$7\r\nSETNAME\r\n$5\r\nmyapp\r\n";
2010 let (cmd, consumed) = Command::parse(data).unwrap();
2011 assert_eq!(
2012 cmd,
2013 Command::Hello {
2014 proto_version: Some(3),
2015 auth: None,
2016 client_name: Some(b"myapp" as &[u8]),
2017 }
2018 );
2019 assert_eq!(consumed, data.len());
2020 }
2021
2022 #[test]
2023 fn test_parse_hello_full() {
2024 let data = b"*7\r\n$5\r\nHELLO\r\n$1\r\n3\r\n$4\r\nAUTH\r\n$4\r\nuser\r\n$4\r\npass\r\n$7\r\nSETNAME\r\n$5\r\nmyapp\r\n";
2026 let (cmd, consumed) = Command::parse(data).unwrap();
2027 assert_eq!(
2028 cmd,
2029 Command::Hello {
2030 proto_version: Some(3),
2031 auth: Some((b"user" as &[u8], b"pass" as &[u8])),
2032 client_name: Some(b"myapp" as &[u8]),
2033 }
2034 );
2035 assert_eq!(consumed, data.len());
2036 }
2037
2038 #[test]
2039 fn test_hello_command_name() {
2040 assert_eq!(
2041 Command::Hello {
2042 proto_version: Some(3),
2043 auth: None,
2044 client_name: None
2045 }
2046 .name(),
2047 "HELLO"
2048 );
2049 }
2050
2051 #[test]
2052 fn test_parse_hello_auth_missing_args() {
2053 let data = b"*4\r\n$5\r\nHELLO\r\n$1\r\n3\r\n$4\r\nAUTH\r\n$4\r\nuser\r\n";
2055 let result = Command::parse(data);
2056 assert!(matches!(result, Err(ParseError::Protocol(_))));
2057 }
2058
2059 #[test]
2060 fn test_parse_hello_setname_missing_args() {
2061 let data = b"*3\r\n$5\r\nHELLO\r\n$1\r\n3\r\n$7\r\nSETNAME\r\n";
2063 let result = Command::parse(data);
2064 assert!(matches!(result, Err(ParseError::Protocol(_))));
2065 }
2066
2067 #[test]
2068 fn test_parse_hello_unknown_option() {
2069 let data = b"*3\r\n$5\r\nHELLO\r\n$1\r\n3\r\n$7\r\nINVALID\r\n";
2071 let result = Command::parse(data);
2072 assert!(matches!(result, Err(ParseError::Protocol(_))));
2073 }
2074
2075 #[test]
2076 fn test_parse_hello_invalid_version() {
2077 let data = b"*2\r\n$5\r\nHELLO\r\n$3\r\nabc\r\n";
2079 let result = Command::parse(data);
2080 assert!(matches!(result, Err(ParseError::Protocol(_))));
2081 }
2082
2083 #[test]
2084 fn test_parse_hello_invalid_utf8_option() {
2085 let data = b"*3\r\n$5\r\nHELLO\r\n$1\r\n3\r\n$2\r\n\xff\xfe\r\n";
2087 let result = Command::parse(data);
2088 assert!(matches!(result, Err(ParseError::Protocol(_))));
2089 }
2090
2091 #[test]
2092 fn test_parse_hello_invalid_utf8_version() {
2093 let data = b"*2\r\n$5\r\nHELLO\r\n$2\r\n\xff\xfe\r\n";
2095 let result = Command::parse(data);
2096 assert!(matches!(result, Err(ParseError::Protocol(_))));
2097 }
2098 }
2099}