Skip to main content

resp_proto/
request.rs

1//! Client-side request encoding.
2//!
3//! This module provides efficient encoding of Redis commands for client applications.
4//! Commands are encoded as RESP arrays of bulk strings.
5
6use std::io::Write;
7
8/// A request builder for encoding Redis commands.
9///
10/// This provides a fluent interface for building and encoding commands.
11///
12/// # Example
13///
14/// ```
15/// use resp_proto::Request;
16///
17/// let mut buf = vec![0u8; 1024];
18///
19/// // Simple GET
20/// let len = Request::get(b"mykey").encode(&mut buf);
21///
22/// // SET with expiration
23/// let len = Request::set(b"mykey", b"myvalue").ex(3600).encode(&mut buf);
24/// ```
25#[derive(Debug, Clone)]
26pub struct Request<'a> {
27    args: Vec<&'a [u8]>,
28}
29
30impl<'a> Request<'a> {
31    /// Create a new request with the given arguments.
32    #[inline]
33    pub fn new(args: Vec<&'a [u8]>) -> Self {
34        Self { args }
35    }
36
37    /// Create a PING command.
38    #[inline]
39    pub fn ping() -> Self {
40        Self {
41            args: vec![b"PING"],
42        }
43    }
44
45    /// Create a GET command.
46    #[inline]
47    pub fn get(key: &'a [u8]) -> Self {
48        Self {
49            args: vec![b"GET", key],
50        }
51    }
52
53    /// Create a SET command.
54    #[inline]
55    pub fn set(key: &'a [u8], value: &'a [u8]) -> SetRequest<'a> {
56        SetRequest {
57            key,
58            value,
59            ex: None,
60            px: None,
61            nx: false,
62            xx: false,
63        }
64    }
65
66    /// Create a DEL command.
67    #[inline]
68    pub fn del(key: &'a [u8]) -> Self {
69        Self {
70            args: vec![b"DEL", key],
71        }
72    }
73
74    /// Create a MGET command (multiple keys).
75    #[inline]
76    pub fn mget(keys: &[&'a [u8]]) -> Self {
77        let mut args = Vec::with_capacity(1 + keys.len());
78        args.push(b"MGET" as &[u8]);
79        args.extend_from_slice(keys);
80        Self { args }
81    }
82
83    /// Create a CONFIG GET command.
84    #[inline]
85    pub fn config_get(key: &'a [u8]) -> Self {
86        Self {
87            args: vec![b"CONFIG", b"GET", key],
88        }
89    }
90
91    /// Create a CONFIG SET command.
92    #[inline]
93    pub fn config_set(key: &'a [u8], value: &'a [u8]) -> Self {
94        Self {
95            args: vec![b"CONFIG", b"SET", key, value],
96        }
97    }
98
99    /// Create a FLUSHDB command.
100    #[inline]
101    pub fn flushdb() -> Self {
102        Self {
103            args: vec![b"FLUSHDB"],
104        }
105    }
106
107    /// Create a FLUSHALL command.
108    #[inline]
109    pub fn flushall() -> Self {
110        Self {
111            args: vec![b"FLUSHALL"],
112        }
113    }
114
115    /// Create a CLUSTER SLOTS command.
116    #[inline]
117    pub fn cluster_slots() -> Self {
118        Self {
119            args: vec![b"CLUSTER", b"SLOTS"],
120        }
121    }
122
123    /// Create a CLUSTER NODES command.
124    #[inline]
125    pub fn cluster_nodes() -> Self {
126        Self {
127            args: vec![b"CLUSTER", b"NODES"],
128        }
129    }
130
131    /// Create a CLUSTER INFO command.
132    #[inline]
133    pub fn cluster_info() -> Self {
134        Self {
135            args: vec![b"CLUSTER", b"INFO"],
136        }
137    }
138
139    /// Create a CLUSTER MYID command.
140    #[inline]
141    pub fn cluster_myid() -> Self {
142        Self {
143            args: vec![b"CLUSTER", b"MYID"],
144        }
145    }
146
147    /// Create an ASKING command.
148    #[inline]
149    pub fn asking() -> Self {
150        Self {
151            args: vec![b"ASKING"],
152        }
153    }
154
155    /// Create a READONLY command.
156    #[inline]
157    pub fn readonly() -> Self {
158        Self {
159            args: vec![b"READONLY"],
160        }
161    }
162
163    /// Create a READWRITE command.
164    #[inline]
165    pub fn readwrite() -> Self {
166        Self {
167            args: vec![b"READWRITE"],
168        }
169    }
170
171    /// Create a custom command with arbitrary arguments.
172    #[inline]
173    pub fn cmd(name: &'a [u8]) -> Self {
174        Self { args: vec![name] }
175    }
176
177    /// Add an argument to the command.
178    #[inline]
179    pub fn arg(mut self, arg: &'a [u8]) -> Self {
180        self.args.push(arg);
181        self
182    }
183
184    /// Encode this request into a buffer.
185    ///
186    /// Returns the number of bytes written.
187    ///
188    /// # Panics
189    ///
190    /// Panics if the buffer is too small.
191    #[inline]
192    pub fn encode(&self, buf: &mut [u8]) -> usize {
193        encode_command(buf, &self.args)
194    }
195
196    /// Calculate the encoded length of this request.
197    pub fn encoded_len(&self) -> usize {
198        let mut len = 0;
199
200        // Array header: *<count>\r\n
201        let mut count_buf = itoa::Buffer::new();
202        len += 1 + count_buf.format(self.args.len()).len() + 2;
203
204        // Each argument: $<len>\r\n<data>\r\n
205        for arg in &self.args {
206            let mut arg_len_buf = itoa::Buffer::new();
207            len += 1 + arg_len_buf.format(arg.len()).len() + 2 + arg.len() + 2;
208        }
209
210        len
211    }
212}
213
214/// Builder for SET commands with options.
215#[derive(Debug, Clone)]
216pub struct SetRequest<'a> {
217    key: &'a [u8],
218    value: &'a [u8],
219    ex: Option<u64>,
220    px: Option<u64>,
221    nx: bool,
222    xx: bool,
223}
224
225impl<'a> SetRequest<'a> {
226    /// Set expiration in seconds (EX option).
227    #[inline]
228    pub fn ex(mut self, seconds: u64) -> Self {
229        self.ex = Some(seconds);
230        self.px = None; // EX and PX are mutually exclusive
231        self
232    }
233
234    /// Set expiration in milliseconds (PX option).
235    #[inline]
236    pub fn px(mut self, milliseconds: u64) -> Self {
237        self.px = Some(milliseconds);
238        self.ex = None; // EX and PX are mutually exclusive
239        self
240    }
241
242    /// Only set if key does not exist (NX option).
243    #[inline]
244    pub fn nx(mut self) -> Self {
245        self.nx = true;
246        self.xx = false; // NX and XX are mutually exclusive
247        self
248    }
249
250    /// Only set if key exists (XX option).
251    #[inline]
252    pub fn xx(mut self) -> Self {
253        self.xx = true;
254        self.nx = false; // NX and XX are mutually exclusive
255        self
256    }
257
258    /// Encode this SET request as `(prefix, suffix)` for scatter-gather sends.
259    ///
260    /// The caller supplies the value bytes separately. The full RESP encoding is
261    /// `[prefix, value, suffix].concat()`.
262    ///
263    /// - **prefix**: array header + SET bulk string + key bulk string + value length header (`$Lv\r\n`)
264    /// - **suffix**: `\r\n` after the value + any option bulk strings (EX/PX/NX/XX)
265    pub fn encode_parts(&self) -> (Vec<u8>, Vec<u8>) {
266        let mut ex_str = itoa::Buffer::new();
267        let mut px_str = itoa::Buffer::new();
268
269        // Count total args: SET + key + value + options
270        let mut arg_count: usize = 3;
271        if self.ex.is_some() || self.px.is_some() {
272            arg_count += 2;
273        }
274        if self.nx || self.xx {
275            arg_count += 1;
276        }
277
278        // Build prefix: *<count>\r\n $3\r\nSET\r\n $<klen>\r\n<key>\r\n $<vlen>\r\n
279        let mut prefix = Vec::new();
280        let mut count_buf = itoa::Buffer::new();
281        write!(prefix, "*{}\r\n", count_buf.format(arg_count)).unwrap();
282        // SET bulk string
283        write!(prefix, "$3\r\nSET\r\n").unwrap();
284        // Key bulk string
285        let mut klen_buf = itoa::Buffer::new();
286        write!(prefix, "${}\r\n", klen_buf.format(self.key.len())).unwrap();
287        prefix.extend_from_slice(self.key);
288        write!(prefix, "\r\n").unwrap();
289        // Value length header (value data supplied by caller)
290        let mut vlen_buf = itoa::Buffer::new();
291        write!(prefix, "${}\r\n", vlen_buf.format(self.value.len())).unwrap();
292
293        // Build suffix: \r\n + option bulk strings
294        let mut suffix = Vec::new();
295        write!(suffix, "\r\n").unwrap();
296        if let Some(seconds) = self.ex {
297            let s = ex_str.format(seconds);
298            let mut slen_buf = itoa::Buffer::new();
299            write!(suffix, "$2\r\nEX\r\n${}\r\n", slen_buf.format(s.len())).unwrap();
300            suffix.extend_from_slice(s.as_bytes());
301            write!(suffix, "\r\n").unwrap();
302        } else if let Some(millis) = self.px {
303            let s = px_str.format(millis);
304            let mut slen_buf = itoa::Buffer::new();
305            write!(suffix, "$2\r\nPX\r\n${}\r\n", slen_buf.format(s.len())).unwrap();
306            suffix.extend_from_slice(s.as_bytes());
307            write!(suffix, "\r\n").unwrap();
308        }
309        if self.nx {
310            write!(suffix, "$2\r\nNX\r\n").unwrap();
311        } else if self.xx {
312            write!(suffix, "$2\r\nXX\r\n").unwrap();
313        }
314
315        (prefix, suffix)
316    }
317
318    /// Encode this SET request into a buffer.
319    ///
320    /// Returns the number of bytes written.
321    #[inline]
322    pub fn encode(&self, buf: &mut [u8]) -> usize {
323        // Build argument list
324        let mut ex_str = itoa::Buffer::new();
325        let mut px_str = itoa::Buffer::new();
326
327        let mut args: Vec<&[u8]> = vec![b"SET", self.key, self.value];
328
329        if let Some(seconds) = self.ex {
330            args.push(b"EX");
331            args.push(ex_str.format(seconds).as_bytes());
332        } else if let Some(millis) = self.px {
333            args.push(b"PX");
334            args.push(px_str.format(millis).as_bytes());
335        }
336
337        if self.nx {
338            args.push(b"NX");
339        } else if self.xx {
340            args.push(b"XX");
341        }
342
343        encode_command(buf, &args)
344    }
345
346    /// Calculate the encoded length of this request.
347    pub fn encoded_len(&self) -> usize {
348        // Build the argument list to compute exact length
349        let mut ex_str = itoa::Buffer::new();
350        let mut px_str = itoa::Buffer::new();
351
352        let mut args: Vec<&[u8]> = vec![b"SET", self.key, self.value];
353
354        if let Some(seconds) = self.ex {
355            args.push(b"EX");
356            args.push(ex_str.format(seconds).as_bytes());
357        } else if let Some(millis) = self.px {
358            args.push(b"PX");
359            args.push(px_str.format(millis).as_bytes());
360        }
361
362        if self.nx {
363            args.push(b"NX");
364        } else if self.xx {
365            args.push(b"XX");
366        }
367
368        // Calculate exact length using same logic as Request::encoded_len()
369        let mut len = 0;
370
371        // Array header: *<count>\r\n
372        let mut count_buf = itoa::Buffer::new();
373        len += 1 + count_buf.format(args.len()).len() + 2;
374
375        // Each argument: $<len>\r\n<data>\r\n
376        for arg in &args {
377            let mut arg_len_buf = itoa::Buffer::new();
378            len += 1 + arg_len_buf.format(arg.len()).len() + 2 + arg.len() + 2;
379        }
380
381        len
382    }
383}
384
385/// Encode a command (array of bulk strings) into a buffer.
386///
387/// Returns the number of bytes written.
388#[inline]
389pub fn encode_command(buf: &mut [u8], args: &[&[u8]]) -> usize {
390    let mut pos = 0;
391
392    // Write array header: *<count>\r\n
393    buf[pos] = b'*';
394    pos += 1;
395    let mut cursor = std::io::Cursor::new(&mut buf[pos..]);
396    write!(cursor, "{}\r\n", args.len()).unwrap();
397    pos += cursor.position() as usize;
398
399    // Write each argument as bulk string
400    for arg in args {
401        // $<len>\r\n
402        buf[pos] = b'$';
403        pos += 1;
404        let mut cursor = std::io::Cursor::new(&mut buf[pos..]);
405        write!(cursor, "{}\r\n", arg.len()).unwrap();
406        pos += cursor.position() as usize;
407
408        // <data>\r\n
409        buf[pos..pos + arg.len()].copy_from_slice(arg);
410        pos += arg.len();
411        buf[pos] = b'\r';
412        buf[pos + 1] = b'\n';
413        pos += 2;
414    }
415
416    pos
417}
418
419#[cfg(test)]
420mod tests {
421    use super::*;
422
423    #[test]
424    fn test_encode_ping() {
425        let mut buf = [0u8; 64];
426        let len = Request::ping().encode(&mut buf);
427        assert_eq!(&buf[..len], b"*1\r\n$4\r\nPING\r\n");
428    }
429
430    #[test]
431    fn test_encode_get() {
432        let mut buf = [0u8; 64];
433        let len = Request::get(b"mykey").encode(&mut buf);
434        assert_eq!(&buf[..len], b"*2\r\n$3\r\nGET\r\n$5\r\nmykey\r\n");
435    }
436
437    #[test]
438    fn test_encode_set() {
439        let mut buf = [0u8; 64];
440        let len = Request::set(b"mykey", b"myvalue").encode(&mut buf);
441        assert_eq!(
442            &buf[..len],
443            b"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"
444        );
445    }
446
447    #[test]
448    fn test_encode_set_ex() {
449        let mut buf = [0u8; 128];
450        let len = Request::set(b"mykey", b"myvalue").ex(3600).encode(&mut buf);
451        assert_eq!(
452            &buf[..len],
453            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"
454        );
455    }
456
457    #[test]
458    fn test_encode_set_px() {
459        let mut buf = [0u8; 128];
460        let len = Request::set(b"key", b"val").px(1000).encode(&mut buf);
461        assert!(std::str::from_utf8(&buf[..len]).unwrap().contains("PX"));
462    }
463
464    #[test]
465    fn test_encode_set_nx() {
466        let mut buf = [0u8; 128];
467        let len = Request::set(b"key", b"val").nx().encode(&mut buf);
468        assert!(std::str::from_utf8(&buf[..len]).unwrap().contains("NX"));
469    }
470
471    #[test]
472    fn test_encode_del() {
473        let mut buf = [0u8; 64];
474        let len = Request::del(b"mykey").encode(&mut buf);
475        assert_eq!(&buf[..len], b"*2\r\n$3\r\nDEL\r\n$5\r\nmykey\r\n");
476    }
477
478    #[test]
479    fn test_encode_mget() {
480        let mut buf = [0u8; 128];
481        let keys: &[&[u8]] = &[b"key1", b"key2", b"key3"];
482        let len = Request::mget(keys).encode(&mut buf);
483        assert_eq!(
484            &buf[..len],
485            b"*4\r\n$4\r\nMGET\r\n$4\r\nkey1\r\n$4\r\nkey2\r\n$4\r\nkey3\r\n"
486        );
487    }
488
489    #[test]
490    fn test_encode_flushdb() {
491        let mut buf = [0u8; 64];
492        let len = Request::flushdb().encode(&mut buf);
493        assert_eq!(&buf[..len], b"*1\r\n$7\r\nFLUSHDB\r\n");
494    }
495
496    #[test]
497    fn test_encode_custom() {
498        let mut buf = [0u8; 64];
499        let len = Request::cmd(b"INCR").arg(b"counter").encode(&mut buf);
500        assert_eq!(&buf[..len], b"*2\r\n$4\r\nINCR\r\n$7\r\ncounter\r\n");
501    }
502
503    #[test]
504    fn test_encoded_len() {
505        let requests: Vec<Request> = vec![
506            Request::ping(),
507            Request::get(b"mykey"),
508            Request::del(b"test"),
509        ];
510
511        for req in requests {
512            let mut buf = [0u8; 256];
513            let actual_len = req.encode(&mut buf);
514            assert_eq!(req.encoded_len(), actual_len);
515        }
516    }
517
518    #[test]
519    fn test_request_new() {
520        let args: Vec<&[u8]> = vec![b"CUSTOM", b"arg1", b"arg2"];
521        let req = Request::new(args);
522        let mut buf = [0u8; 128];
523        let len = req.encode(&mut buf);
524        assert!(len > 0);
525        assert!(std::str::from_utf8(&buf[..len]).unwrap().contains("CUSTOM"));
526    }
527
528    #[test]
529    fn test_encode_config_get() {
530        let mut buf = [0u8; 128];
531        let len = Request::config_get(b"maxclients").encode(&mut buf);
532        let encoded = std::str::from_utf8(&buf[..len]).unwrap();
533        assert!(encoded.contains("CONFIG"));
534        assert!(encoded.contains("GET"));
535        assert!(encoded.contains("maxclients"));
536    }
537
538    #[test]
539    fn test_encode_config_set() {
540        let mut buf = [0u8; 128];
541        let len = Request::config_set(b"maxclients", b"100").encode(&mut buf);
542        let encoded = std::str::from_utf8(&buf[..len]).unwrap();
543        assert!(encoded.contains("CONFIG"));
544        assert!(encoded.contains("SET"));
545        assert!(encoded.contains("maxclients"));
546        assert!(encoded.contains("100"));
547    }
548
549    #[test]
550    fn test_encode_flushall() {
551        let mut buf = [0u8; 64];
552        let len = Request::flushall().encode(&mut buf);
553        assert_eq!(&buf[..len], b"*1\r\n$8\r\nFLUSHALL\r\n");
554    }
555
556    #[test]
557    fn test_encode_set_xx() {
558        let mut buf = [0u8; 128];
559        let len = Request::set(b"key", b"val").xx().encode(&mut buf);
560        assert!(std::str::from_utf8(&buf[..len]).unwrap().contains("XX"));
561    }
562
563    #[test]
564    fn test_set_px_overrides_ex() {
565        // Setting PX should clear EX
566        let mut buf = [0u8; 128];
567        let len = Request::set(b"key", b"val")
568            .ex(100)
569            .px(5000)
570            .encode(&mut buf);
571        let encoded = std::str::from_utf8(&buf[..len]).unwrap();
572        assert!(encoded.contains("PX"));
573        assert!(!encoded.contains("EX\r\n")); // Should not have EX as separate arg
574    }
575
576    #[test]
577    fn test_set_ex_overrides_px() {
578        // Setting EX should clear PX
579        let mut buf = [0u8; 128];
580        let len = Request::set(b"key", b"val")
581            .px(5000)
582            .ex(100)
583            .encode(&mut buf);
584        let encoded = std::str::from_utf8(&buf[..len]).unwrap();
585        assert!(encoded.contains("EX"));
586        assert!(!encoded.contains("PX"));
587    }
588
589    #[test]
590    fn test_set_xx_overrides_nx() {
591        // Setting XX should clear NX
592        let set_req = Request::set(b"key", b"val").nx().xx();
593        assert!(set_req.xx);
594        assert!(!set_req.nx);
595    }
596
597    #[test]
598    fn test_set_nx_overrides_xx() {
599        // Setting NX should clear XX
600        let set_req = Request::set(b"key", b"val").xx().nx();
601        assert!(set_req.nx);
602        assert!(!set_req.xx);
603    }
604
605    #[test]
606    fn test_set_request_encoded_len() {
607        // Test various SetRequest configurations
608        let configs = vec![
609            Request::set(b"key", b"value"),
610            Request::set(b"key", b"value").ex(3600),
611            Request::set(b"key", b"value").px(5000),
612            Request::set(b"key", b"value").nx(),
613            Request::set(b"key", b"value").xx(),
614            Request::set(b"key", b"value").ex(3600).nx(),
615        ];
616
617        for config in configs {
618            let mut buf = [0u8; 256];
619            let actual_len = config.encode(&mut buf);
620            let estimated_len = config.encoded_len();
621            assert_eq!(
622                estimated_len, actual_len,
623                "encoded_len() should match actual encoded length"
624            );
625        }
626    }
627
628    #[test]
629    fn test_set_request_encoded_len_large_values() {
630        // Test with larger keys and values to ensure length calculation handles
631        // multi-digit lengths correctly
632        let large_key = vec![b'k'; 1000];
633        let large_value = vec![b'v'; 10000];
634
635        let config = Request::set(&large_key, &large_value).ex(86400);
636        let mut buf = vec![0u8; 20000];
637        let actual_len = config.encode(&mut buf);
638        let estimated_len = config.encoded_len();
639        assert_eq!(
640            estimated_len, actual_len,
641            "encoded_len() should match for large values"
642        );
643    }
644
645    #[test]
646    fn test_encode_parts_matches_encode() {
647        let configs: Vec<SetRequest<'_>> = vec![
648            Request::set(b"mykey", b"myvalue"),
649            Request::set(b"k", b"v").ex(3600),
650            Request::set(b"key", b"val").px(1000),
651            Request::set(b"key", b"val").nx(),
652            Request::set(b"key", b"val").xx(),
653            Request::set(b"key", b"val").ex(86400).nx(),
654            Request::set(b"key", b"val").px(500).xx(),
655        ];
656
657        for config in &configs {
658            // Full encode
659            let mut buf = vec![0u8; 512];
660            let len = config.encode(&mut buf);
661            let full = &buf[..len];
662
663            // Parts encode
664            let (prefix, suffix) = config.encode_parts();
665            let mut assembled = Vec::new();
666            assembled.extend_from_slice(&prefix);
667            assembled.extend_from_slice(config.value);
668            assembled.extend_from_slice(&suffix);
669
670            assert_eq!(
671                assembled, full,
672                "encode_parts concat must match encode() for {:?}",
673                config
674            );
675        }
676    }
677
678    #[test]
679    fn test_request_debug() {
680        let req = Request::ping();
681        let debug_str = format!("{:?}", req);
682        assert!(debug_str.contains("Request"));
683    }
684
685    #[test]
686    fn test_request_clone() {
687        let req1 = Request::get(b"mykey");
688        let req2 = req1.clone();
689        let mut buf1 = [0u8; 64];
690        let mut buf2 = [0u8; 64];
691        let len1 = req1.encode(&mut buf1);
692        let len2 = req2.encode(&mut buf2);
693        assert_eq!(&buf1[..len1], &buf2[..len2]);
694    }
695
696    #[test]
697    fn test_set_request_debug() {
698        let set_req = Request::set(b"key", b"value");
699        let debug_str = format!("{:?}", set_req);
700        assert!(debug_str.contains("SetRequest"));
701    }
702
703    #[test]
704    fn test_set_request_clone() {
705        let set1 = Request::set(b"key", b"value").ex(100);
706        let set2 = set1.clone();
707        let mut buf1 = [0u8; 128];
708        let mut buf2 = [0u8; 128];
709        let len1 = set1.encode(&mut buf1);
710        let len2 = set2.encode(&mut buf2);
711        assert_eq!(&buf1[..len1], &buf2[..len2]);
712    }
713
714    #[test]
715    fn test_encode_cluster_slots() {
716        let mut buf = [0u8; 128];
717        let len = Request::cluster_slots().encode(&mut buf);
718        assert_eq!(&buf[..len], b"*2\r\n$7\r\nCLUSTER\r\n$5\r\nSLOTS\r\n");
719    }
720
721    #[test]
722    fn test_encode_cluster_nodes() {
723        let mut buf = [0u8; 128];
724        let len = Request::cluster_nodes().encode(&mut buf);
725        assert_eq!(&buf[..len], b"*2\r\n$7\r\nCLUSTER\r\n$5\r\nNODES\r\n");
726    }
727
728    #[test]
729    fn test_encode_cluster_info() {
730        let mut buf = [0u8; 128];
731        let len = Request::cluster_info().encode(&mut buf);
732        assert_eq!(&buf[..len], b"*2\r\n$7\r\nCLUSTER\r\n$4\r\nINFO\r\n");
733    }
734
735    #[test]
736    fn test_encode_cluster_myid() {
737        let mut buf = [0u8; 128];
738        let len = Request::cluster_myid().encode(&mut buf);
739        assert_eq!(&buf[..len], b"*2\r\n$7\r\nCLUSTER\r\n$4\r\nMYID\r\n");
740    }
741
742    #[test]
743    fn test_encode_asking() {
744        let mut buf = [0u8; 64];
745        let len = Request::asking().encode(&mut buf);
746        assert_eq!(&buf[..len], b"*1\r\n$6\r\nASKING\r\n");
747    }
748
749    #[test]
750    fn test_encode_readonly() {
751        let mut buf = [0u8; 64];
752        let len = Request::readonly().encode(&mut buf);
753        assert_eq!(&buf[..len], b"*1\r\n$8\r\nREADONLY\r\n");
754    }
755
756    #[test]
757    fn test_encode_readwrite() {
758        let mut buf = [0u8; 64];
759        let len = Request::readwrite().encode(&mut buf);
760        assert_eq!(&buf[..len], b"*1\r\n$9\r\nREADWRITE\r\n");
761    }
762
763    #[test]
764    fn test_mget_empty() {
765        let keys: &[&[u8]] = &[];
766        let req = Request::mget(keys);
767        let mut buf = [0u8; 64];
768        let len = req.encode(&mut buf);
769        // Should just be MGET with no keys
770        assert_eq!(&buf[..len], b"*1\r\n$4\r\nMGET\r\n");
771    }
772}