Skip to main content

memcache_proto/
request.rs

1//! Client-side request encoding for Memcache ASCII protocol.
2//!
3//! This module provides encoding of Memcache commands for client applications.
4
5use std::io::Write;
6
7/// A request builder for encoding Memcache commands.
8#[derive(Debug, Clone)]
9pub enum Request<'a> {
10    /// GET command: `get <key>\r\n`
11    Get { key: &'a [u8] },
12    /// Multi-GET command: `get <key1> <key2> ...\r\n`
13    Gets { keys: &'a [&'a [u8]] },
14    /// SET command: `set <key> <flags> <exptime> <bytes>\r\n<data>\r\n`
15    Set {
16        key: &'a [u8],
17        value: &'a [u8],
18        flags: u32,
19        exptime: u32,
20    },
21    /// ADD command: `add <key> <flags> <exptime> <bytes>\r\n<data>\r\n`
22    ///
23    /// Stores the item only if the key does not already exist.
24    Add {
25        key: &'a [u8],
26        value: &'a [u8],
27        flags: u32,
28        exptime: u32,
29    },
30    /// REPLACE command: `replace <key> <flags> <exptime> <bytes>\r\n<data>\r\n`
31    ///
32    /// Stores the item only if the key already exists.
33    Replace {
34        key: &'a [u8],
35        value: &'a [u8],
36        flags: u32,
37        exptime: u32,
38    },
39    /// INCR command: `incr <key> <delta>\r\n`
40    Incr { key: &'a [u8], delta: u64 },
41    /// DECR command: `decr <key> <delta>\r\n`
42    Decr { key: &'a [u8], delta: u64 },
43    /// APPEND command: `append <key> 0 0 <bytes>\r\n<data>\r\n`
44    ///
45    /// Appends data to an existing item's value.
46    Append { key: &'a [u8], value: &'a [u8] },
47    /// PREPEND command: `prepend <key> 0 0 <bytes>\r\n<data>\r\n`
48    ///
49    /// Prepends data to an existing item's value.
50    Prepend { key: &'a [u8], value: &'a [u8] },
51    /// CAS command: `cas <key> <flags> <exptime> <bytes> <cas_unique>\r\n<data>\r\n`
52    ///
53    /// Compare-and-swap: stores only if the CAS token matches.
54    Cas {
55        key: &'a [u8],
56        value: &'a [u8],
57        flags: u32,
58        exptime: u32,
59        cas_unique: u64,
60    },
61    /// DELETE command: `delete <key>\r\n`
62    Delete { key: &'a [u8] },
63    /// FLUSH_ALL command: `flush_all\r\n`
64    FlushAll,
65    /// VERSION command: `version\r\n`
66    Version,
67    /// QUIT command: `quit\r\n`
68    Quit,
69}
70
71impl<'a> Request<'a> {
72    /// Create a GET request.
73    #[inline]
74    pub fn get(key: &'a [u8]) -> Self {
75        Request::Get { key }
76    }
77
78    /// Create a multi-GET request.
79    #[inline]
80    pub fn gets(keys: &'a [&'a [u8]]) -> Self {
81        Request::Gets { keys }
82    }
83
84    /// Create a SET request.
85    #[inline]
86    pub fn set(key: &'a [u8], value: &'a [u8]) -> SetRequest<'a> {
87        SetRequest {
88            key,
89            value,
90            flags: 0,
91            exptime: 0,
92        }
93    }
94
95    /// Create an ADD request (store only if key does not exist).
96    #[inline]
97    pub fn add(key: &'a [u8], value: &'a [u8]) -> AddRequest<'a> {
98        AddRequest {
99            key,
100            value,
101            flags: 0,
102            exptime: 0,
103        }
104    }
105
106    /// Create a REPLACE request (store only if key already exists).
107    #[inline]
108    pub fn replace(key: &'a [u8], value: &'a [u8]) -> ReplaceRequest<'a> {
109        ReplaceRequest {
110            key,
111            value,
112            flags: 0,
113            exptime: 0,
114        }
115    }
116
117    /// Create an INCR request.
118    #[inline]
119    pub fn incr(key: &'a [u8], delta: u64) -> Self {
120        Request::Incr { key, delta }
121    }
122
123    /// Create a DECR request.
124    #[inline]
125    pub fn decr(key: &'a [u8], delta: u64) -> Self {
126        Request::Decr { key, delta }
127    }
128
129    /// Create an APPEND request.
130    #[inline]
131    pub fn append(key: &'a [u8], value: &'a [u8]) -> Self {
132        Request::Append { key, value }
133    }
134
135    /// Create a PREPEND request.
136    #[inline]
137    pub fn prepend(key: &'a [u8], value: &'a [u8]) -> Self {
138        Request::Prepend { key, value }
139    }
140
141    /// Create a CAS (compare-and-swap) request.
142    #[inline]
143    pub fn cas(key: &'a [u8], value: &'a [u8], cas_unique: u64) -> Self {
144        Request::Cas {
145            key,
146            value,
147            flags: 0,
148            exptime: 0,
149            cas_unique,
150        }
151    }
152
153    /// Create a DELETE request.
154    #[inline]
155    pub fn delete(key: &'a [u8]) -> Self {
156        Request::Delete { key }
157    }
158
159    /// Create a FLUSH_ALL request.
160    #[inline]
161    pub fn flush_all() -> Self {
162        Request::FlushAll
163    }
164
165    /// Create a VERSION request.
166    #[inline]
167    pub fn version() -> Self {
168        Request::Version
169    }
170
171    /// Create a QUIT request.
172    #[inline]
173    pub fn quit() -> Self {
174        Request::Quit
175    }
176
177    /// Encode this request into a buffer.
178    ///
179    /// Returns the number of bytes written.
180    pub fn encode(&self, buf: &mut [u8]) -> usize {
181        match self {
182            Request::Get { key } => encode_get(buf, key),
183            Request::Gets { keys } => encode_gets(buf, keys),
184            Request::Set {
185                key,
186                value,
187                flags,
188                exptime,
189            } => encode_storage(buf, b"set", key, value, *flags, *exptime),
190            Request::Add {
191                key,
192                value,
193                flags,
194                exptime,
195            } => encode_storage(buf, b"add", key, value, *flags, *exptime),
196            Request::Replace {
197                key,
198                value,
199                flags,
200                exptime,
201            } => encode_storage(buf, b"replace", key, value, *flags, *exptime),
202            Request::Incr { key, delta } => encode_incr_decr(buf, b"incr", key, *delta),
203            Request::Decr { key, delta } => encode_incr_decr(buf, b"decr", key, *delta),
204            Request::Append { key, value } => encode_storage(buf, b"append", key, value, 0, 0),
205            Request::Prepend { key, value } => encode_storage(buf, b"prepend", key, value, 0, 0),
206            Request::Cas {
207                key,
208                value,
209                flags,
210                exptime,
211                cas_unique,
212            } => encode_cas(buf, key, value, *flags, *exptime, *cas_unique),
213            Request::Delete { key } => encode_delete(buf, key),
214            Request::FlushAll => encode_simple(buf, b"flush_all"),
215            Request::Version => encode_simple(buf, b"version"),
216            Request::Quit => encode_simple(buf, b"quit"),
217        }
218    }
219}
220
221/// Builder for SET requests with optional flags and exptime.
222#[derive(Debug, Clone)]
223pub struct SetRequest<'a> {
224    key: &'a [u8],
225    value: &'a [u8],
226    flags: u32,
227    exptime: u32,
228}
229
230impl<'a> SetRequest<'a> {
231    /// Set the flags value.
232    #[inline]
233    pub fn flags(mut self, flags: u32) -> Self {
234        self.flags = flags;
235        self
236    }
237
238    /// Set the expiration time in seconds.
239    #[inline]
240    pub fn exptime(mut self, exptime: u32) -> Self {
241        self.exptime = exptime;
242        self
243    }
244
245    /// Build the final request.
246    #[inline]
247    pub fn build(self) -> Request<'a> {
248        Request::Set {
249            key: self.key,
250            value: self.value,
251            flags: self.flags,
252            exptime: self.exptime,
253        }
254    }
255
256    /// Encode this request directly into a buffer.
257    ///
258    /// Returns the number of bytes written.
259    #[inline]
260    pub fn encode(&self, buf: &mut [u8]) -> usize {
261        encode_storage(buf, b"set", self.key, self.value, self.flags, self.exptime)
262    }
263}
264
265/// Builder for ADD requests with optional flags and exptime.
266#[derive(Debug, Clone)]
267pub struct AddRequest<'a> {
268    key: &'a [u8],
269    value: &'a [u8],
270    flags: u32,
271    exptime: u32,
272}
273
274impl<'a> AddRequest<'a> {
275    /// Set the flags value.
276    #[inline]
277    pub fn flags(mut self, flags: u32) -> Self {
278        self.flags = flags;
279        self
280    }
281
282    /// Set the expiration time in seconds.
283    #[inline]
284    pub fn exptime(mut self, exptime: u32) -> Self {
285        self.exptime = exptime;
286        self
287    }
288
289    /// Build the final request.
290    #[inline]
291    pub fn build(self) -> Request<'a> {
292        Request::Add {
293            key: self.key,
294            value: self.value,
295            flags: self.flags,
296            exptime: self.exptime,
297        }
298    }
299
300    /// Encode this request directly into a buffer.
301    ///
302    /// Returns the number of bytes written.
303    #[inline]
304    pub fn encode(&self, buf: &mut [u8]) -> usize {
305        encode_storage(buf, b"add", self.key, self.value, self.flags, self.exptime)
306    }
307}
308
309/// Builder for REPLACE requests with optional flags and exptime.
310#[derive(Debug, Clone)]
311pub struct ReplaceRequest<'a> {
312    key: &'a [u8],
313    value: &'a [u8],
314    flags: u32,
315    exptime: u32,
316}
317
318impl<'a> ReplaceRequest<'a> {
319    /// Set the flags value.
320    #[inline]
321    pub fn flags(mut self, flags: u32) -> Self {
322        self.flags = flags;
323        self
324    }
325
326    /// Set the expiration time in seconds.
327    #[inline]
328    pub fn exptime(mut self, exptime: u32) -> Self {
329        self.exptime = exptime;
330        self
331    }
332
333    /// Build the final request.
334    #[inline]
335    pub fn build(self) -> Request<'a> {
336        Request::Replace {
337            key: self.key,
338            value: self.value,
339            flags: self.flags,
340            exptime: self.exptime,
341        }
342    }
343
344    /// Encode this request directly into a buffer.
345    ///
346    /// Returns the number of bytes written.
347    #[inline]
348    pub fn encode(&self, buf: &mut [u8]) -> usize {
349        encode_storage(
350            buf,
351            b"replace",
352            self.key,
353            self.value,
354            self.flags,
355            self.exptime,
356        )
357    }
358}
359
360/// Encode a GET command: `get <key>\r\n`
361fn encode_get(buf: &mut [u8], key: &[u8]) -> usize {
362    let mut pos = 0;
363    buf[pos..pos + 4].copy_from_slice(b"get ");
364    pos += 4;
365    buf[pos..pos + key.len()].copy_from_slice(key);
366    pos += key.len();
367    buf[pos..pos + 2].copy_from_slice(b"\r\n");
368    pos + 2
369}
370
371/// Encode a multi-GET command with CAS: `gets <key1> <key2> ...\r\n`
372fn encode_gets(buf: &mut [u8], keys: &[&[u8]]) -> usize {
373    if keys.is_empty() {
374        return 0;
375    }
376
377    let mut pos = 0;
378    buf[pos..pos + 4].copy_from_slice(b"gets");
379    pos += 4;
380
381    for key in keys {
382        buf[pos] = b' ';
383        pos += 1;
384        buf[pos..pos + key.len()].copy_from_slice(key);
385        pos += key.len();
386    }
387
388    buf[pos..pos + 2].copy_from_slice(b"\r\n");
389    pos + 2
390}
391
392/// Encode a storage command: `<cmd> <key> <flags> <exptime> <bytes>\r\n<data>\r\n`
393///
394/// Used for SET, ADD, REPLACE, etc. — all share the same wire format.
395fn encode_storage(
396    buf: &mut [u8],
397    cmd: &[u8],
398    key: &[u8],
399    value: &[u8],
400    flags: u32,
401    exptime: u32,
402) -> usize {
403    let mut pos = 0;
404
405    // <cmd> <key>
406    buf[pos..pos + cmd.len()].copy_from_slice(cmd);
407    pos += cmd.len();
408    buf[pos] = b' ';
409    pos += 1;
410    buf[pos..pos + key.len()].copy_from_slice(key);
411    pos += key.len();
412    buf[pos] = b' ';
413    pos += 1;
414
415    // <flags> <exptime> <bytes>\r\n
416    let mut cursor = std::io::Cursor::new(&mut buf[pos..]);
417    write!(cursor, "{} {} {}\r\n", flags, exptime, value.len()).unwrap();
418    pos += cursor.position() as usize;
419
420    // <data>\r\n
421    buf[pos..pos + value.len()].copy_from_slice(value);
422    pos += value.len();
423    buf[pos..pos + 2].copy_from_slice(b"\r\n");
424    pos + 2
425}
426
427/// Encode a DELETE command: `delete <key>\r\n`
428fn encode_delete(buf: &mut [u8], key: &[u8]) -> usize {
429    let mut pos = 0;
430    buf[pos..pos + 7].copy_from_slice(b"delete ");
431    pos += 7;
432    buf[pos..pos + key.len()].copy_from_slice(key);
433    pos += key.len();
434    buf[pos..pos + 2].copy_from_slice(b"\r\n");
435    pos + 2
436}
437
438/// Encode a CAS command: `cas <key> <flags> <exptime> <bytes> <cas_unique>\r\n<data>\r\n`
439fn encode_cas(
440    buf: &mut [u8],
441    key: &[u8],
442    value: &[u8],
443    flags: u32,
444    exptime: u32,
445    cas_unique: u64,
446) -> usize {
447    let mut pos = 0;
448
449    // cas <key>
450    buf[pos..pos + 4].copy_from_slice(b"cas ");
451    pos += 4;
452    buf[pos..pos + key.len()].copy_from_slice(key);
453    pos += key.len();
454    buf[pos] = b' ';
455    pos += 1;
456
457    // <flags> <exptime> <bytes> <cas_unique>\r\n
458    let mut cursor = std::io::Cursor::new(&mut buf[pos..]);
459    write!(
460        cursor,
461        "{} {} {} {}\r\n",
462        flags,
463        exptime,
464        value.len(),
465        cas_unique
466    )
467    .unwrap();
468    pos += cursor.position() as usize;
469
470    // <data>\r\n
471    buf[pos..pos + value.len()].copy_from_slice(value);
472    pos += value.len();
473    buf[pos..pos + 2].copy_from_slice(b"\r\n");
474    pos + 2
475}
476
477/// Encode an INCR/DECR command: `<cmd> <key> <delta>\r\n`
478fn encode_incr_decr(buf: &mut [u8], cmd: &[u8], key: &[u8], delta: u64) -> usize {
479    let mut pos = 0;
480    buf[pos..pos + cmd.len()].copy_from_slice(cmd);
481    pos += cmd.len();
482    buf[pos] = b' ';
483    pos += 1;
484    buf[pos..pos + key.len()].copy_from_slice(key);
485    pos += key.len();
486    buf[pos] = b' ';
487    pos += 1;
488
489    let mut cursor = std::io::Cursor::new(&mut buf[pos..]);
490    write!(cursor, "{}\r\n", delta).unwrap();
491    pos += cursor.position() as usize;
492    pos
493}
494
495/// Encode a simple command with no arguments.
496fn encode_simple(buf: &mut [u8], cmd: &[u8]) -> usize {
497    buf[..cmd.len()].copy_from_slice(cmd);
498    buf[cmd.len()..cmd.len() + 2].copy_from_slice(b"\r\n");
499    cmd.len() + 2
500}
501
502#[cfg(test)]
503mod tests {
504    use super::*;
505
506    #[test]
507    fn test_encode_get() {
508        let mut buf = [0u8; 64];
509        let len = Request::get(b"mykey").encode(&mut buf);
510        assert_eq!(&buf[..len], b"get mykey\r\n");
511    }
512
513    #[test]
514    fn test_encode_gets() {
515        let mut buf = [0u8; 64];
516        let keys: &[&[u8]] = &[b"key1", b"key2", b"key3"];
517        let len = Request::gets(keys).encode(&mut buf);
518        assert_eq!(&buf[..len], b"gets key1 key2 key3\r\n");
519    }
520
521    #[test]
522    fn test_encode_set() {
523        let mut buf = [0u8; 64];
524        let len = Request::set(b"mykey", b"myvalue").encode(&mut buf);
525        assert_eq!(&buf[..len], b"set mykey 0 0 7\r\nmyvalue\r\n");
526    }
527
528    #[test]
529    fn test_encode_set_with_options() {
530        let mut buf = [0u8; 64];
531        let len = Request::set(b"mykey", b"myvalue")
532            .flags(123)
533            .exptime(3600)
534            .encode(&mut buf);
535        assert_eq!(&buf[..len], b"set mykey 123 3600 7\r\nmyvalue\r\n");
536    }
537
538    #[test]
539    fn test_encode_delete() {
540        let mut buf = [0u8; 64];
541        let len = Request::delete(b"mykey").encode(&mut buf);
542        assert_eq!(&buf[..len], b"delete mykey\r\n");
543    }
544
545    #[test]
546    fn test_encode_flush_all() {
547        let mut buf = [0u8; 64];
548        let len = Request::flush_all().encode(&mut buf);
549        assert_eq!(&buf[..len], b"flush_all\r\n");
550    }
551
552    #[test]
553    fn test_encode_version() {
554        let mut buf = [0u8; 64];
555        let len = Request::version().encode(&mut buf);
556        assert_eq!(&buf[..len], b"version\r\n");
557    }
558
559    #[test]
560    fn test_encode_quit() {
561        let mut buf = [0u8; 64];
562        let len = Request::quit().encode(&mut buf);
563        assert_eq!(&buf[..len], b"quit\r\n");
564    }
565
566    // Additional tests for improved coverage
567
568    #[test]
569    fn test_set_request_build() {
570        let mut buf = [0u8; 64];
571        let request = Request::set(b"mykey", b"myvalue")
572            .flags(42)
573            .exptime(600)
574            .build();
575        let len = request.encode(&mut buf);
576        assert_eq!(&buf[..len], b"set mykey 42 600 7\r\nmyvalue\r\n");
577    }
578
579    #[test]
580    fn test_encode_gets_empty() {
581        let mut buf = [0u8; 64];
582        let keys: &[&[u8]] = &[];
583        let len = Request::gets(keys).encode(&mut buf);
584        assert_eq!(len, 0);
585    }
586
587    #[test]
588    fn test_encode_gets_single() {
589        let mut buf = [0u8; 64];
590        let keys: &[&[u8]] = &[b"single"];
591        let len = Request::gets(keys).encode(&mut buf);
592        assert_eq!(&buf[..len], b"gets single\r\n");
593    }
594
595    #[test]
596    fn test_request_debug() {
597        let req = Request::get(b"key");
598        let debug_str = format!("{:?}", req);
599        assert!(debug_str.contains("Get"));
600    }
601
602    #[test]
603    fn test_request_clone() {
604        let req1 = Request::get(b"key");
605        let req2 = req1.clone();
606        // Both should encode the same
607        let mut buf1 = [0u8; 64];
608        let mut buf2 = [0u8; 64];
609        let len1 = req1.encode(&mut buf1);
610        let len2 = req2.encode(&mut buf2);
611        assert_eq!(&buf1[..len1], &buf2[..len2]);
612    }
613
614    #[test]
615    fn test_set_request_debug() {
616        let req = Request::set(b"key", b"value");
617        let debug_str = format!("{:?}", req);
618        assert!(debug_str.contains("SetRequest"));
619    }
620
621    #[test]
622    fn test_set_request_clone() {
623        let req1 = Request::set(b"key", b"value").flags(1);
624        let req2 = req1.clone();
625        let mut buf1 = [0u8; 64];
626        let mut buf2 = [0u8; 64];
627        let len1 = req1.encode(&mut buf1);
628        let len2 = req2.encode(&mut buf2);
629        assert_eq!(&buf1[..len1], &buf2[..len2]);
630    }
631
632    #[test]
633    fn test_encode_set_via_request() {
634        let mut buf = [0u8; 64];
635        let request = Request::Set {
636            key: b"k",
637            value: b"v",
638            flags: 0,
639            exptime: 0,
640        };
641        let len = request.encode(&mut buf);
642        assert_eq!(&buf[..len], b"set k 0 0 1\r\nv\r\n");
643    }
644
645    #[test]
646    fn test_encode_gets_via_request() {
647        let mut buf = [0u8; 64];
648        let keys: &[&[u8]] = &[b"a", b"b"];
649        let request = Request::Gets { keys };
650        let len = request.encode(&mut buf);
651        assert_eq!(&buf[..len], b"gets a b\r\n");
652    }
653
654    #[test]
655    fn test_encode_add() {
656        let mut buf = [0u8; 64];
657        let len = Request::add(b"mykey", b"myvalue").encode(&mut buf);
658        assert_eq!(&buf[..len], b"add mykey 0 0 7\r\nmyvalue\r\n");
659    }
660
661    #[test]
662    fn test_encode_add_with_options() {
663        let mut buf = [0u8; 64];
664        let len = Request::add(b"mykey", b"myvalue")
665            .flags(99)
666            .exptime(300)
667            .encode(&mut buf);
668        assert_eq!(&buf[..len], b"add mykey 99 300 7\r\nmyvalue\r\n");
669    }
670
671    #[test]
672    fn test_add_request_build() {
673        let mut buf = [0u8; 64];
674        let request = Request::add(b"mykey", b"myvalue")
675            .flags(42)
676            .exptime(600)
677            .build();
678        let len = request.encode(&mut buf);
679        assert_eq!(&buf[..len], b"add mykey 42 600 7\r\nmyvalue\r\n");
680    }
681
682    #[test]
683    fn test_encode_add_via_request() {
684        let mut buf = [0u8; 64];
685        let request = Request::Add {
686            key: b"k",
687            value: b"v",
688            flags: 0,
689            exptime: 0,
690        };
691        let len = request.encode(&mut buf);
692        assert_eq!(&buf[..len], b"add k 0 0 1\r\nv\r\n");
693    }
694
695    #[test]
696    fn test_encode_replace() {
697        let mut buf = [0u8; 64];
698        let len = Request::replace(b"mykey", b"myvalue").encode(&mut buf);
699        assert_eq!(&buf[..len], b"replace mykey 0 0 7\r\nmyvalue\r\n");
700    }
701
702    #[test]
703    fn test_encode_replace_with_options() {
704        let mut buf = [0u8; 64];
705        let len = Request::replace(b"mykey", b"myvalue")
706            .flags(99)
707            .exptime(300)
708            .encode(&mut buf);
709        assert_eq!(&buf[..len], b"replace mykey 99 300 7\r\nmyvalue\r\n");
710    }
711
712    #[test]
713    fn test_replace_request_build() {
714        let mut buf = [0u8; 64];
715        let request = Request::replace(b"mykey", b"myvalue")
716            .flags(42)
717            .exptime(600)
718            .build();
719        let len = request.encode(&mut buf);
720        assert_eq!(&buf[..len], b"replace mykey 42 600 7\r\nmyvalue\r\n");
721    }
722
723    #[test]
724    fn test_encode_replace_via_request() {
725        let mut buf = [0u8; 64];
726        let request = Request::Replace {
727            key: b"k",
728            value: b"v",
729            flags: 0,
730            exptime: 0,
731        };
732        let len = request.encode(&mut buf);
733        assert_eq!(&buf[..len], b"replace k 0 0 1\r\nv\r\n");
734    }
735
736    #[test]
737    fn test_encode_incr() {
738        let mut buf = [0u8; 64];
739        let len = Request::incr(b"counter", 1).encode(&mut buf);
740        assert_eq!(&buf[..len], b"incr counter 1\r\n");
741    }
742
743    #[test]
744    fn test_encode_incr_large_delta() {
745        let mut buf = [0u8; 64];
746        let len = Request::incr(b"counter", 12345).encode(&mut buf);
747        assert_eq!(&buf[..len], b"incr counter 12345\r\n");
748    }
749
750    #[test]
751    fn test_encode_decr() {
752        let mut buf = [0u8; 64];
753        let len = Request::decr(b"counter", 5).encode(&mut buf);
754        assert_eq!(&buf[..len], b"decr counter 5\r\n");
755    }
756
757    #[test]
758    fn test_encode_decr_large_delta() {
759        let mut buf = [0u8; 64];
760        let len = Request::decr(b"counter", 99999).encode(&mut buf);
761        assert_eq!(&buf[..len], b"decr counter 99999\r\n");
762    }
763
764    #[test]
765    fn test_encode_append() {
766        let mut buf = [0u8; 64];
767        let len = Request::append(b"mykey", b"extra").encode(&mut buf);
768        assert_eq!(&buf[..len], b"append mykey 0 0 5\r\nextra\r\n");
769    }
770
771    #[test]
772    fn test_encode_prepend() {
773        let mut buf = [0u8; 64];
774        let len = Request::prepend(b"mykey", b"prefix").encode(&mut buf);
775        assert_eq!(&buf[..len], b"prepend mykey 0 0 6\r\nprefix\r\n");
776    }
777
778    #[test]
779    fn test_encode_cas() {
780        let mut buf = [0u8; 128];
781        let len = Request::cas(b"mykey", b"myvalue", 12345).encode(&mut buf);
782        assert_eq!(&buf[..len], b"cas mykey 0 0 7 12345\r\nmyvalue\r\n");
783    }
784
785    #[test]
786    fn test_encode_cas_via_request() {
787        let mut buf = [0u8; 128];
788        let request = Request::Cas {
789            key: b"k",
790            value: b"v",
791            flags: 42,
792            exptime: 600,
793            cas_unique: 99,
794        };
795        let len = request.encode(&mut buf);
796        assert_eq!(&buf[..len], b"cas k 42 600 1 99\r\nv\r\n");
797    }
798}