Repository: aisk/rust-memcache (language: rust)

Issue:
Inconsistency between Binary and ASCII protocols
When using binary protocol, `add`ing a key when it already exists results in `Err(MemcacheError::ServerError(0x02))` (`ResponseStatus::KeyExists` error) but with ASCII protocol, the library returns `Ok(())`. Issue is [here](https://github.com/aisk/rust-memcache/blob/66bd9671df31377f3edf9e4fce7d7aa11615d23f/src/protocol/ascii.rs#L83)


Discussion / hints:
Confirmed, I think the binary protocol implementation is right, and it should be a bug on ASCII protocol.
Yes, I will work on a fix.

Fix patch:
diff --git a/src/protocol/ascii.rs b/src/protocol/ascii.rs
index cd62157..45a46d4 100644
--- a/src/protocol/ascii.rs
+++ b/src/protocol/ascii.rs
@@ -2,6 +2,7 @@ use std::collections::HashMap;
 use std::fmt;
 use std::io::{BufRead, BufReader, Read, Write};
 
+use super::ResponseStatus;
 use client::Stats;
 use error::MemcacheError;
 use stream::Stream;
@@ -95,8 +96,10 @@ impl AsciiProtocol<Stream> {
             return Ok(true);
         } else if s == "NOT_STORED\r\n" {
             return Ok(false);
-        } else if command == StoreCommand::Cas && (s == "EXISTS\r\n" || s == "NOT_FOUND\r\n") {
-            return Ok(false);
+        } else if s == "EXISTS\r\n" {
+            return Err(MemcacheError::from(ResponseStatus::KeyExists as u16));
+        } else if s == "NOT_FOUND\r\n" {
+            return Err(MemcacheError::from(ResponseStatus::KeyNotFound as u16));
         } else {
             return Err(MemcacheError::ClientError("invalid server response".into()));
         }
@@ -250,7 +253,15 @@ impl AsciiProtocol<Stream> {
             cas: Some(cas),
             ..Default::default()
         };
-        self.store(StoreCommand::Cas, key, value, &options)
+        match self.store(StoreCommand::Cas, key, value, &options) {
+            Ok(t) => Ok(t),
+            Err(MemcacheError::ServerError(e))
+                if e == ResponseStatus::KeyExists as u16 || e == ResponseStatus::KeyNotFound as u16 =>
+            {
+                Ok(false)
+            }
+            e => e,
+        }
     }
 
     pub(super) fn set<V: ToMemcacheValue<Stream>>(
diff --git a/src/protocol/binary_packet.rs b/src/protocol/binary_packet.rs
index 0367b44..97da39a 100644
--- a/src/protocol/binary_packet.rs
+++ b/src/protocol/binary_packet.rs
@@ -1,3 +1,4 @@
+use super::ResponseStatus;
 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
 use error::MemcacheError;
 use std::collections::HashMap;
@@ -29,16 +30,6 @@ pub enum Magic {
     Response = 0x81,
 }
 
-#[allow(dead_code)]
-pub enum ResponseStatus {
-    NoError = 0x00,
-    KeyNotFound = 0x01,
-    KeyExists = 0x02,
-    ValueTooLarge = 0x03,
-    InvalidArguments = 0x04,
-    AuthenticationRequired = 0x20,
-}
-
 #[derive(Debug, Default)]
 pub struct PacketHeader {
     pub magic: u8,
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index 0d68aac..ddfa5ce 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -17,6 +17,16 @@ pub enum Protocol {
     Binary(BinaryProtocol),
 }
 
+#[allow(dead_code)]
+pub enum ResponseStatus {
+    NoError = 0x00,
+    KeyNotFound = 0x01,
+    KeyExists = 0x02,
+    ValueTooLarge = 0x03,
+    InvalidArguments = 0x04,
+    AuthenticationRequired = 0x20,
+}
+
 #[enum_dispatch(Protocol)]
 pub trait ProtocolTrait {
     fn auth(&mut self, username: &str, password: &str) -> Result<(), MemcacheError>;


Test patch: