Skip to main content

resp_proto/
command.rs

1//! Server-side command parsing.
2//!
3//! This module parses RESP protocol data into structured commands for server implementations.
4//! It provides zero-copy parsing where possible, with command arguments referencing the input buffer.
5
6use crate::error::ParseError;
7use crate::value::ParseOptions;
8
9/// A parsed Redis command with references to the original buffer.
10///
11/// Commands are parsed with zero-copy semantics - the key and value fields
12/// reference slices of the original input buffer.
13#[derive(Debug, Clone, PartialEq, Eq)]
14pub enum Command<'a> {
15    /// PING command
16    Ping,
17    /// GET key
18    Get { key: &'a [u8] },
19    /// SET key value [EX seconds] [PX milliseconds] [NX|XX]
20    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
29    Del { key: &'a [u8] },
30    /// MGET key [key ...]
31    MGet { keys: Vec<&'a [u8]> },
32    /// CONFIG subcommand [args...]
33    Config {
34        subcommand: &'a [u8],
35        args: Vec<&'a [u8]>,
36    },
37    /// CLUSTER subcommand [args...]
38    Cluster {
39        subcommand: &'a [u8],
40        args: Vec<&'a [u8]>,
41    },
42    /// ASKING — used before retrying a command after an ASK redirect
43    Asking,
44    /// READONLY — enable read queries on a replica node
45    ReadOnly,
46    /// READWRITE — disable READONLY mode
47    ReadWrite,
48    /// FLUSHDB
49    FlushDb,
50    /// FLUSHALL
51    FlushAll,
52    /// INCR key - Increment the integer value of a key by one
53    Incr { key: &'a [u8] },
54    /// DECR key - Decrement the integer value of a key by one
55    Decr { key: &'a [u8] },
56    /// INCRBY key delta - Increment the integer value of a key by delta
57    IncrBy { key: &'a [u8], delta: i64 },
58    /// DECRBY key delta - Decrement the integer value of a key by delta
59    DecrBy { key: &'a [u8], delta: i64 },
60    /// APPEND key value - Append a value to a key
61    Append { key: &'a [u8], value: &'a [u8] },
62    /// HELLO [protover [AUTH username password] [SETNAME clientname]]
63    /// Used for RESP3 protocol negotiation.
64    #[cfg(feature = "resp3")]
65    Hello {
66        /// Protocol version (2 or 3)
67        proto_version: Option<u8>,
68        /// AUTH username and password
69        auth: Option<(&'a [u8], &'a [u8])>,
70        /// Client name
71        client_name: Option<&'a [u8]>,
72    },
73
74    // ========================================================================
75    // Hash Commands
76    // ========================================================================
77    /// HSET key field value [field value ...] - Set field(s) in a hash
78    HSet {
79        key: &'a [u8],
80        fields: Vec<(&'a [u8], &'a [u8])>,
81    },
82    /// HGET key field - Get a field from a hash
83    HGet { key: &'a [u8], field: &'a [u8] },
84    /// HMGET key field [field ...] - Get multiple fields from a hash
85    HMGet {
86        key: &'a [u8],
87        fields: Vec<&'a [u8]>,
88    },
89    /// HGETALL key - Get all fields and values from a hash
90    HGetAll { key: &'a [u8] },
91    /// HDEL key field [field ...] - Delete field(s) from a hash
92    HDel {
93        key: &'a [u8],
94        fields: Vec<&'a [u8]>,
95    },
96    /// HEXISTS key field - Check if field exists in a hash
97    HExists { key: &'a [u8], field: &'a [u8] },
98    /// HLEN key - Get the number of fields in a hash
99    HLen { key: &'a [u8] },
100    /// HKEYS key - Get all field names in a hash
101    HKeys { key: &'a [u8] },
102    /// HVALS key - Get all values in a hash
103    HVals { key: &'a [u8] },
104    /// HSETNX key field value - Set field only if it doesn't exist
105    HSetNx {
106        key: &'a [u8],
107        field: &'a [u8],
108        value: &'a [u8],
109    },
110    /// HINCRBY key field increment - Increment hash field by integer
111    HIncrBy {
112        key: &'a [u8],
113        field: &'a [u8],
114        delta: i64,
115    },
116
117    // ========================================================================
118    // List Commands
119    // ========================================================================
120    /// LPUSH key element [element ...] - Push to left of list
121    LPush {
122        key: &'a [u8],
123        values: Vec<&'a [u8]>,
124    },
125    /// RPUSH key element [element ...] - Push to right of list
126    RPush {
127        key: &'a [u8],
128        values: Vec<&'a [u8]>,
129    },
130    /// LPOP key `[count]` - Pop from left of list
131    LPop { key: &'a [u8], count: Option<usize> },
132    /// RPOP key `[count]` - Pop from right of list
133    RPop { key: &'a [u8], count: Option<usize> },
134    /// LRANGE key start stop - Get range of elements
135    LRange {
136        key: &'a [u8],
137        start: i64,
138        stop: i64,
139    },
140    /// LLEN key - Get list length
141    LLen { key: &'a [u8] },
142    /// LINDEX key index - Get element by index
143    LIndex { key: &'a [u8], index: i64 },
144    /// LSET key index element - Set element by index
145    LSet {
146        key: &'a [u8],
147        index: i64,
148        value: &'a [u8],
149    },
150    /// LTRIM key start stop - Trim list to range
151    LTrim {
152        key: &'a [u8],
153        start: i64,
154        stop: i64,
155    },
156    /// LPUSHX key element [element ...] - Push to left only if list exists
157    LPushX {
158        key: &'a [u8],
159        values: Vec<&'a [u8]>,
160    },
161    /// RPUSHX key element [element ...] - Push to right only if list exists
162    RPushX {
163        key: &'a [u8],
164        values: Vec<&'a [u8]>,
165    },
166
167    // ========================================================================
168    // Set Commands
169    // ========================================================================
170    /// SADD key member [member ...] - Add members to set
171    SAdd {
172        key: &'a [u8],
173        members: Vec<&'a [u8]>,
174    },
175    /// SREM key member [member ...] - Remove members from set
176    SRem {
177        key: &'a [u8],
178        members: Vec<&'a [u8]>,
179    },
180    /// SMEMBERS key - Get all members of set
181    SMembers { key: &'a [u8] },
182    /// SISMEMBER key member - Check if member exists in set
183    SIsMember { key: &'a [u8], member: &'a [u8] },
184    /// SMISMEMBER key member [member ...] - Check multiple members
185    SMisMember {
186        key: &'a [u8],
187        members: Vec<&'a [u8]>,
188    },
189    /// SCARD key - Get set cardinality
190    SCard { key: &'a [u8] },
191    /// SPOP key `[count]` - Remove and return random member(s)
192    SPop { key: &'a [u8], count: Option<usize> },
193    /// SRANDMEMBER key `[count]` - Get random member(s) without removal
194    SRandMember { key: &'a [u8], count: Option<i64> },
195
196    // ========================================================================
197    // Type Command
198    // ========================================================================
199    /// TYPE key - Get the type of key
200    Type { key: &'a [u8] },
201}
202
203impl<'a> Command<'a> {
204    /// Parse a command from a byte buffer using default limits.
205    ///
206    /// Returns the parsed command and the number of bytes consumed.
207    ///
208    /// # Zero-copy
209    ///
210    /// The returned command contains references to the input buffer, so the buffer
211    /// must outlive the command. This avoids allocation for keys and values.
212    ///
213    /// # Errors
214    ///
215    /// Returns `ParseError::Incomplete` if more data is needed.
216    /// Returns other errors for malformed or unknown commands.
217    #[inline]
218    pub fn parse(buffer: &'a [u8]) -> Result<(Self, usize), ParseError> {
219        Self::parse_with_options(buffer, &ParseOptions::default())
220    }
221
222    /// Parse a command from a byte buffer with custom options.
223    ///
224    /// This is useful for setting custom limits on bulk string size to prevent
225    /// denial-of-service attacks or to enforce server-side value size limits.
226    ///
227    /// # Zero-copy
228    ///
229    /// The returned command contains references to the input buffer, so the buffer
230    /// must outlive the command. This avoids allocation for keys and values.
231    ///
232    /// # Errors
233    ///
234    /// Returns `ParseError::Incomplete` if more data is needed.
235    /// Returns `ParseError::BulkStringTooLong` if a bulk string exceeds the limit.
236    /// Returns other errors for malformed or unknown commands.
237    #[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        // Read array header
245        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        // Read array length
253        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        // Reject unreasonably large arrays to prevent OOM attacks
260        const MAX_ARRAY_LEN: usize = 1024 * 1024; // 1M elements max
261        if count > MAX_ARRAY_LEN {
262            return Err(ParseError::Protocol("array too large".to_string()));
263        }
264
265        // Read command name
266        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        // Parse command based on name (case-insensitive)
271        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            // ================================================================
506            // Hash Commands
507            // ================================================================
508            _ 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            // ================================================================
644            // List Commands
645            // ================================================================
646            _ 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            // ================================================================
840            // Set Commands
841            // ================================================================
842            _ 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            // ================================================================
966            // Type Command
967            // ================================================================
968            _ 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                // Parse optional protocol version
987                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                // Parse optional AUTH and SETNAME
1000                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    /// Returns the command name as a string.
1048    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            // Hash commands
1068            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            // List commands
1080            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            // Set commands
1092            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            // Type command
1101            Command::Type { .. } => "TYPE",
1102            #[cfg(feature = "resp3")]
1103            Command::Hello { .. } => "HELLO",
1104        }
1105    }
1106}
1107
1108/// A cursor for reading RESP data from a buffer.
1109struct 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        // Limit integer length to prevent overflow during parsing.
1149        // usize::MAX is at most 20 digits, so 19 is a safe limit.
1150        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        // Check bulk string length limit
1181        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        // Verify CRLF
1196        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"; // GET with no key
1381        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        // SET k v EX (missing the expiration value)
1440        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        // SET k v PX (missing the expiration value)
1448        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        // SET k v EX invalid
1456        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        // PING with extra argument
1464        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        // DEL with no key
1472        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        // MGET with no keys
1480        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        // CONFIG with no subcommand
1488        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        // SET with only key
1496        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        // Command not starting with array
1504        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        // Empty array (0 elements)
1512        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        // Array with length that's too large (>19 digits)
1568        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        // Array length with non-digit character
1576        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        // Array length that causes overflow
1584        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        // Bulk string with wrong trailing bytes
1592        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        // Command name not a bulk string
1600        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        // Command name with invalid UTF-8
1608        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        // SET with invalid UTF-8 in option
1616        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        // SET with invalid UTF-8 in TTL value
1624        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        // SET k v PX invalid
1632        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        // Bulk string with incomplete data
1653        let data = b"*2\r\n$3\r\nGET\r\n$100\r\nmykey\r\n"; // claims 100 bytes but only 5
1654        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        // MGET with huge array count should reject as protocol error, not OOM
1661        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        // Array larger than MAX_ARRAY_LEN (1M) should be rejected
1669        let data = b"*1048577\r\n$4\r\nPING\r\n"; // 1M + 1
1670        let result = Command::parse(data);
1671        assert!(matches!(result, Err(ParseError::Protocol(_))));
1672    }
1673
1674    // ========================================================================
1675    // INCR/DECR Tests
1676    // ========================================================================
1677
1678    #[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    // ========================================================================
1848    // Cluster Command Tests
1849    // ========================================================================
1850
1851    #[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    // ========================================================================
1952    // RESP3 Tests
1953    // ========================================================================
1954
1955    #[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            // HELLO 3 AUTH username password
1992            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            // HELLO 3 SETNAME myapp
2009            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            // HELLO 3 AUTH user pass SETNAME myapp
2025            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            // HELLO 3 AUTH user (missing password)
2054            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            // HELLO 3 SETNAME (missing name)
2062            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            // HELLO 3 INVALID
2070            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            // HELLO abc (invalid version)
2078            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            // HELLO 3 with invalid UTF-8 option
2086            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            // HELLO with invalid UTF-8 version
2094            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}