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

Issue:
Refactor the error system
Goal:

- Unifi the error with ASCII protocol and binary protocol;
- Add specify error like `KeyNotFound`.


Discussion / hints:
I will work on this.

Fix patch:
diff --git a/src/client.rs b/src/client.rs
index 13087ec..96d75e1 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -66,11 +66,7 @@ impl Client {
         let urls = target.get_urls();
         let mut connections = vec![];
         for url in urls {
-            let parsed = match Url::parse(url.as_str()) {
-                Ok(v) => v,
-                Err(_) => return Err(MemcacheError::ClientError("Invalid memcache URL".into())),
-            };
-
+            let parsed = Url::parse(url.as_str())?;
             let mut connection = Connection::connect(&parsed)?;
 
             if parsed.has_authority() && parsed.username() != "" && parsed.password().is_some() {
diff --git a/src/error.rs b/src/error.rs
index 0290587..e220d0a 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,86 +1,268 @@
+use std::borrow::Cow;
 use std::error;
 use std::fmt;
 use std::io;
 use std::num;
 use std::str;
 use std::string;
+use url;
+
+/// Client-side errors
+#[derive(Debug, PartialEq)]
+pub enum ClientError {
+    /// The key provided was longer than 250 bytes.
+    KeyTooLong,
+    /// The server returned an error prefixed with CLIENT_ERROR in response to a command.
+    Error(Cow<'static, str>),
+}
+
+impl fmt::Display for ClientError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            ClientError::KeyTooLong => write!(f, "The provided key was too long."),
+            ClientError::Error(s) => write!(f, "{}", s),
+        }
+    }
+}
+
+impl From<ClientError> for MemcacheError {
+    fn from(err: ClientError) -> Self {
+        MemcacheError::ClientError(err)
+    }
+}
+
+/// Server-side errors
+#[derive(Debug)]
+pub enum ServerError {
+    /// When using binary protocol, the server returned magic byte other
+    /// than 0x81 in the response packet.
+    BadMagic(u8),
+    /// The client did not expect this response from the server.
+    BadResponse(String),
+    /// The server returned an error prefixed with SERVER_ERROR in response to a command.
+    Error(String),
+}
+
+impl fmt::Display for ServerError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            ServerError::BadMagic(e) => write!(f, "Expected 0x81 as magic in response header, but found: {:x}", e),
+            ServerError::BadResponse(s) => write!(f, "Unexpected: {} in response", s),
+            ServerError::Error(s) => write!(f, "{}", s),
+        }
+    }
+}
+
+/// Command specific errors.
+#[derive(Debug, PartialEq)]
+pub enum CommandError {
+    /// The client tried to set a key which already existed in the server.
+    KeyExists,
+    /// The client tried to set a key which does not exist in the server.
+    KeyNotFound,
+    /// The value for a key was too large. The limit is usually 1MB.
+    ValueTooLarge,
+    /// Invalid arguments were passed to the command.
+    InvalidArguments,
+    /// The server requires authentication.
+    AuthenticationRequired,
+    /// When using binary protocol, the server returned an unknown response status.
+    Unknown(u16),
+    /// The client sent an invalid command to the server.
+    InvalidCommand,
+}
+
+impl MemcacheError {
+    pub(crate) fn try_from(s: String) -> Result<String, MemcacheError> {
+        if s == "ERROR\r\n" {
+            Err(CommandError::InvalidCommand)?
+        } else if s.starts_with("CLIENT_ERROR") {
+            Err(ClientError::from(s))?
+        } else if s.starts_with("SERVER_ERROR") {
+            Err(ServerError::from(s))?
+        } else if s == "NOT_FOUND\r\n" {
+            Err(CommandError::KeyNotFound)?
+        } else if s == "EXISTS\r\n" {
+            Err(CommandError::KeyExists)?
+        } else {
+            Ok(s)
+        }
+    }
+}
+
+impl From<String> for ClientError {
+    fn from(s: String) -> Self {
+        ClientError::Error(Cow::Owned(s))
+    }
+}
+
+impl From<String> for ServerError {
+    fn from(s: String) -> Self {
+        ServerError::Error(s)
+    }
+}
+
+impl fmt::Display for CommandError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            CommandError::KeyExists => write!(f, "Key already exists in the server."),
+            CommandError::KeyNotFound => write!(f, "Key was not found in the server."),
+            CommandError::ValueTooLarge => write!(f, "Value was too large."),
+            CommandError::InvalidArguments => write!(f, "Invalid arguments provided."),
+            CommandError::AuthenticationRequired => write!(f, "Authentication required."),
+            CommandError::Unknown(code) => write!(f, "Unknown error occurred with code: {}.", code),
+            CommandError::InvalidCommand => write!(f, "Invalid command sent to the server."),
+        }
+    }
+}
+
+impl From<u16> for CommandError {
+    fn from(status: u16) -> CommandError {
+        match status {
+            0x1 => CommandError::KeyNotFound,
+            0x2 => CommandError::KeyExists,
+            0x3 => CommandError::ValueTooLarge,
+            0x4 => CommandError::InvalidArguments,
+            0x20 => CommandError::AuthenticationRequired,
+            e => CommandError::Unknown(e),
+        }
+    }
+}
+
+impl From<CommandError> for MemcacheError {
+    fn from(err: CommandError) -> Self {
+        MemcacheError::CommandError(err)
+    }
+}
+
+impl From<ServerError> for MemcacheError {
+    fn from(err: ServerError) -> Self {
+        MemcacheError::ServerError(err)
+    }
+}
+
+#[derive(Debug)]
+pub enum ParseError {
+    Bool(str::ParseBoolError),
+    Int(num::ParseIntError),
+    Float(num::ParseFloatError),
+    String(string::FromUtf8Error),
+    Url(url::ParseError),
+}
+
+impl error::Error for ParseError {
+    fn source(&self) -> Option<&(dyn error::Error + 'static)> {
+        match self {
+            ParseError::Bool(ref e) => e.source(),
+            ParseError::Int(ref e) => e.source(),
+            ParseError::Float(ref e) => e.source(),
+            ParseError::String(ref e) => e.source(),
+            ParseError::Url(ref e) => e.source(),
+        }
+    }
+}
+
+impl fmt::Display for ParseError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        match self {
+            ParseError::Bool(ref e) => e.fmt(f),
+            ParseError::Int(ref e) => e.fmt(f),
+            ParseError::Float(ref e) => e.fmt(f),
+            ParseError::String(ref e) => e.fmt(f),
+            ParseError::Url(ref e) => e.fmt(f),
+        }
+    }
+}
+
+impl From<ParseError> for MemcacheError {
+    fn from(err: ParseError) -> Self {
+        MemcacheError::ParseError(err)
+    }
+}
+
+impl From<string::FromUtf8Error> for MemcacheError {
+    fn from(err: string::FromUtf8Error) -> MemcacheError {
+        ParseError::String(err).into()
+    }
+}
+impl From<num::ParseIntError> for MemcacheError {
+    fn from(err: num::ParseIntError) -> MemcacheError {
+        ParseError::Int(err).into()
+    }
+}
+
+impl From<num::ParseFloatError> for MemcacheError {
+    fn from(err: num::ParseFloatError) -> MemcacheError {
+        ParseError::Float(err).into()
+    }
+}
+
+impl From<url::ParseError> for MemcacheError {
+    fn from(err: url::ParseError) -> MemcacheError {
+        ParseError::Url(err).into()
+    }
+}
+
+impl From<str::ParseBoolError> for MemcacheError {
+    fn from(err: str::ParseBoolError) -> MemcacheError {
+        ParseError::Bool(err).into()
+    }
+}
 
 /// Stands for errors raised from rust-memcache
 #[derive(Debug)]
 pub enum MemcacheError {
-    /// Error raised when the provided memcache URL is invalid
+    /// Error raised when the provided memcache URL doesn't have a host name
+    #[cfg(feature = "tls")]
     BadURL(String),
     /// `std::io` related errors.
-    Io(io::Error),
-    /// Error raised when unserialize value data which from memcached to String
-    FromUtf8(string::FromUtf8Error),
+    IOError(io::Error),
+    /// Client Errors
+    ClientError(ClientError),
+    /// Server Errors
+    ServerError(ServerError),
+    /// Command specific Errors
+    CommandError(CommandError),
     #[cfg(feature = "tls")]
     OpensslError(openssl::ssl::HandshakeError<std::net::TcpStream>),
-    ParseIntError(num::ParseIntError),
-    ParseFloatError(num::ParseFloatError),
-    ParseBoolError(str::ParseBoolError),
-    ClientError(String),
-    ServerError(u16),
+    /// Parse errors
+    ParseError(ParseError),
 }
 
 impl fmt::Display for MemcacheError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
             MemcacheError::BadURL(ref s) => s.fmt(f),
-            MemcacheError::Io(ref err) => err.fmt(f),
-            MemcacheError::FromUtf8(ref err) => err.fmt(f),
+            MemcacheError::IOError(ref err) => err.fmt(f),
             #[cfg(feature = "tls")]
             MemcacheError::OpensslError(ref err) => err.fmt(f),
-            MemcacheError::ParseIntError(ref err) => err.fmt(f),
-            MemcacheError::ParseFloatError(ref err) => err.fmt(f),
-            MemcacheError::ParseBoolError(ref err) => err.fmt(f),
-            MemcacheError::ClientError(ref s) => s.fmt(f),
-            MemcacheError::ServerError(r) => write!(f, "ServerError: {}", r),
+            MemcacheError::ParseError(ref err) => err.fmt(f),
+            MemcacheError::ClientError(ref err) => err.fmt(f),
+            MemcacheError::ServerError(ref err) => err.fmt(f),
+            MemcacheError::CommandError(ref err) => err.fmt(f),
         }
     }
 }
 
 impl error::Error for MemcacheError {
-    fn description(&self) -> &str {
-        match *self {
-            MemcacheError::BadURL(ref s) => s.as_str(),
-            MemcacheError::Io(ref err) => err.description(),
-            MemcacheError::FromUtf8(ref err) => err.description(),
-            #[cfg(feature = "tls")]
-            MemcacheError::OpensslError(ref err) => err.description(),
-            MemcacheError::ParseIntError(ref err) => err.description(),
-            MemcacheError::ParseFloatError(ref err) => err.description(),
-            MemcacheError::ParseBoolError(ref err) => err.description(),
-            MemcacheError::ClientError(ref s) => s.as_str(),
-            MemcacheError::ServerError(_) => "ServerError",
-        }
-    }
-
     fn source(&self) -> Option<&(dyn error::Error + 'static)> {
         match *self {
+            #[cfg(feature = "tls")]
             MemcacheError::BadURL(_) => None,
-            MemcacheError::Io(ref err) => err.source(),
-            MemcacheError::FromUtf8(ref err) => err.source(),
+            MemcacheError::IOError(ref err) => err.source(),
             #[cfg(feature = "tls")]
             MemcacheError::OpensslError(ref err) => err.source(),
-            MemcacheError::ParseIntError(ref err) => err.source(),
-            MemcacheError::ParseFloatError(ref err) => err.source(),
-            MemcacheError::ParseBoolError(ref err) => err.source(),
+            MemcacheError::ParseError(ref p) => p.source(),
             MemcacheError::ClientError(_) => None,
             MemcacheError::ServerError(_) => None,
+            MemcacheError::CommandError(_) => None,
         }
     }
 }
 
 impl From<io::Error> for MemcacheError {
     fn from(err: io::Error) -> MemcacheError {
-        MemcacheError::Io(err)
-    }
-}
-
-impl From<string::FromUtf8Error> for MemcacheError {
-    fn from(err: string::FromUtf8Error) -> MemcacheError {
-        MemcacheError::FromUtf8(err)
+        MemcacheError::IOError(err)
     }
 }
 
@@ -97,33 +279,3 @@ impl From<openssl::ssl::HandshakeError<std::net::TcpStream>> for MemcacheError {
         MemcacheError::OpensslError(err)
     }
 }
-
-impl From<num::ParseIntError> for MemcacheError {
-    fn from(err: num::ParseIntError) -> MemcacheError {
-        MemcacheError::ParseIntError(err)
-    }
-}
-
-impl From<num::ParseFloatError> for MemcacheError {
-    fn from(err: num::ParseFloatError) -> MemcacheError {
-        MemcacheError::ParseFloatError(err)
-    }
-}
-
-impl From<str::ParseBoolError> for MemcacheError {
-    fn from(err: str::ParseBoolError) -> MemcacheError {
-        MemcacheError::ParseBoolError(err)
-    }
-}
-
-impl From<String> for MemcacheError {
-    fn from(s: String) -> MemcacheError {
-        return MemcacheError::ClientError(s);
-    }
-}
-
-impl From<u16> for MemcacheError {
-    fn from(code: u16) -> MemcacheError {
-        return MemcacheError::ServerError(code);
-    }
-}
diff --git a/src/lib.rs b/src/lib.rs
index 490bb72..fef165b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -80,5 +80,5 @@ mod stream;
 mod value;
 
 pub use client::{Client, Connectable};
-pub use error::MemcacheError;
+pub use error::{ClientError, CommandError, MemcacheError, ServerError};
 pub use value::{FromMemcacheValue, FromMemcacheValueExt, ToMemcacheValue};
diff --git a/src/protocol/ascii.rs b/src/protocol/ascii.rs
index 8ae3164..19f8a7a 100644
--- a/src/protocol/ascii.rs
+++ b/src/protocol/ascii.rs
@@ -2,9 +2,10 @@ use std::collections::HashMap;
 use std::fmt;
 use std::io::{BufRead, BufReader, Read, Write};
 
-use super::ResponseStatus;
+use super::check_key_len;
 use client::Stats;
-use error::MemcacheError;
+use error::{ClientError, CommandError, MemcacheError, ServerError};
+use std::borrow::Cow;
 use stream::Stream;
 use value::{FromMemcacheValueExt, ToMemcacheValue};
 
@@ -54,9 +55,7 @@ impl AsciiProtocol<Stream> {
         value: V,
         options: &Options,
     ) -> Result<bool, MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
 
         let mut header = format!(
             "{} {} {} {} {}",
@@ -68,9 +67,9 @@ impl AsciiProtocol<Stream> {
         );
         if command == StoreCommand::Cas {
             if options.cas.is_none() {
-                return Err(MemcacheError::ClientError(String::from(
-                    "cas command should have a casid",
-                )));
+                Err(ClientError::Error(Cow::Borrowed(
+                    "cas_id should be present when using cas command",
+                )))?;
             }
             let cas = options.cas.unwrap();
             header += &format!(" {}", cas);
@@ -90,18 +89,19 @@ impl AsciiProtocol<Stream> {
 
         let mut s = String::new();
         self.reader.read_line(&mut s)?;
-        if is_memcache_error(s.as_str()) {
-            return Err(MemcacheError::from(s));
-        } else if s == "STORED\r\n" {
-            return Ok(true);
-        } else if s == "NOT_STORED\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()));
+        match MemcacheError::try_from(s) {
+            Ok(s) if s == "STORED\r\n" => Ok(true),
+            Ok(s) if s == "NOT_STORED\r\n" => Ok(false),
+            Ok(s) => {
+                if s == "EXISTS\r\n" {
+                    Err(CommandError::KeyExists)?
+                } else if s == "NOT_FOUND\r\n" {
+                    Err(CommandError::KeyNotFound)?
+                } else {
+                    Err(ServerError::BadResponse(s))?
+                }
+            }
+            Err(e) => Err(e),
         }
     }
 
@@ -110,10 +110,9 @@ impl AsciiProtocol<Stream> {
         self.reader.get_mut().flush()?;
         let mut s = String::new();
         self.reader.read_line(&mut s)?;
-        if is_memcache_error(s.as_str()) {
-            return Err(MemcacheError::from(s));
-        } else if !s.starts_with("VERSION") {
-            return Err(MemcacheError::ServerError(0));
+        let s = MemcacheError::try_from(s)?;
+        if !s.starts_with("VERSION") {
+            return Err(ServerError::BadResponse(s).into());
         }
         let s = s.trim_start_matches("VERSION ");
         let s = s.trim_end_matches("\r\n");
@@ -129,10 +128,9 @@ impl AsciiProtocol<Stream> {
         self.reader.get_mut().flush()?;
         let mut s = String::new();
         self.reader.read_line(&mut s)?;
-        if is_memcache_error(s.as_str()) {
-            return Err(MemcacheError::from(s));
-        } else if s != "OK\r\n" {
-            return Err(MemcacheError::ClientError("invalid server response".into()));
+        let s = MemcacheError::try_from(s)?;
+        if s != "OK\r\n" {
+            return Err(ServerError::BadResponse(s).into());
         }
         return Ok(());
     }
@@ -142,10 +140,9 @@ impl AsciiProtocol<Stream> {
         self.reader.get_mut().flush()?;
         let mut s = String::new();
         self.reader.read_line(&mut s)?;
-        if is_memcache_error(s.as_str()) {
-            return Err(MemcacheError::from(s));
-        } else if s != "OK\r\n" {
-            return Err(MemcacheError::ClientError("invalid server response".into()));
+        let s = MemcacheError::try_from(s)?;
+        if s != "OK\r\n" {
+            return Err(ServerError::BadResponse(s).into());
         }
         return Ok(());
     }
@@ -156,21 +153,20 @@ impl AsciiProtocol<Stream> {
         let mut s = String::new();
         self.reader.read_line(&mut s)?;
 
-        if is_memcache_error(s.as_str()) {
-            return Err(MemcacheError::from(s));
-        } else if s.starts_with("END") {
+        let s = MemcacheError::try_from(s)?;
+        if s.starts_with("END") {
             return Ok(None);
         } else if !s.starts_with("VALUE") {
-            return Err(MemcacheError::ClientError("invalid server response".into()));
+            return Err(ServerError::BadResponse(s).into());
         }
 
         let header: Vec<_> = s.trim_end_matches("\r\n").split(" ").collect();
         if header.len() != 4 {
-            return Err(MemcacheError::ClientError("invalid server response".into()));
+            return Err(ServerError::BadResponse(s).into());
         }
 
         if key != header[1] {
-            return Err(MemcacheError::ClientError("invalid server response".into()));
+            return Err(ServerError::BadResponse(s).into());
         }
         let flags = header[2].parse()?;
         let length = header[3].parse()?;
@@ -182,12 +178,12 @@ impl AsciiProtocol<Stream> {
         let mut s = String::new();
         self.reader.read_line(&mut s)?;
         if s != "\r\n" {
-            return Err(MemcacheError::ClientError("invalid server response".into()));
+            return Err(ServerError::BadResponse(s).into());
         }
         s = String::new();
         self.reader.read_line(&mut s)?;
         if s != "END\r\n" {
-            return Err(MemcacheError::ClientError("invalid server response".into()));
+            return Err(ServerError::BadResponse(s).into());
         }
 
         return Ok(Some(FromMemcacheValueExt::from_memcache_value(buffer, flags, None)?));
@@ -201,17 +197,16 @@ impl AsciiProtocol<Stream> {
             let mut s = String::new();
             self.reader.read_line(&mut s)?;
 
-            if is_memcache_error(s.as_str()) {
-                return Err(MemcacheError::from(s));
-            } else if s.starts_with("END") {
+            let s = MemcacheError::try_from(s)?;
+            if s.starts_with("END") {
                 break;
             } else if !s.starts_with("VALUE") {
-                return Err(MemcacheError::ClientError("invalid server response".into()));
+                return Err(ServerError::BadResponse(s).into());
             }
 
             let header: Vec<_> = s.trim_end_matches("\r\n").split(" ").collect();
             if header.len() != 5 {
-                return Err(MemcacheError::ClientError("invalid server response".into()));
+                return Err(ServerError::BadResponse(s).into());
             }
 
             let key = header[1];
@@ -231,7 +226,7 @@ impl AsciiProtocol<Stream> {
             let mut s = String::new();
             self.reader.read_line(&mut s)?;
             if s != "\r\n" {
-                return Err(MemcacheError::ClientError("invalid server response".into()));
+                return Err(ServerError::BadResponse(s).into());
             }
         }
 
@@ -252,9 +247,7 @@ impl AsciiProtocol<Stream> {
         };
         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 =>
-            {
+            Err(MemcacheError::CommandError(e)) if e == CommandError::KeyExists || e == CommandError::KeyNotFound => {
                 Ok(false)
             }
             e => e,
@@ -301,94 +294,70 @@ impl AsciiProtocol<Stream> {
     }
 
     pub(super) fn append<V: ToMemcacheValue<Stream>>(&mut self, key: &str, value: V) -> Result<(), MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         self.store(StoreCommand::Append, key, value, &Default::default())
             .map(|_| ())
     }
 
     pub(super) fn prepend<V: ToMemcacheValue<Stream>>(&mut self, key: &str, value: V) -> Result<(), MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         self.store(StoreCommand::Prepend, key, value, &Default::default())
             .map(|_| ())
     }
 
     pub(super) fn delete(&mut self, key: &str) -> Result<bool, MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         write!(self.reader.get_mut(), "delete {}\r\n", key)?;
         self.reader.get_mut().flush()?;
         let mut s = String::new();
         self.reader.read_line(&mut s)?;
-        if is_memcache_error(s.as_str()) {
-            return Err(MemcacheError::from(s));
-        } else if s == "DELETED\r\n" {
-            return Ok(true);
-        } else if s == "NOT_FOUND\r\n" {
-            return Ok(false);
-        } else {
-            return Err(MemcacheError::ClientError(String::from("invalid server response")));
+        match MemcacheError::try_from(s) {
+            Ok(s) => {
+                if s == "DELETED\r\n" {
+                    Ok(true)
+                } else {
+                    Err(ServerError::BadResponse(s))?
+                }
+            }
+            Err(MemcacheError::CommandError(CommandError::KeyNotFound)) => Ok(false),
+            Err(e) => Err(e),
         }
     }
 
     pub(super) fn increment(&mut self, key: &str, amount: u64) -> Result<u64, MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         write!(self.reader.get_mut(), "incr {} {}\r\n", key, amount)?;
         let mut s = String::new();
         self.reader.read_line(&mut s)?;
-        if is_memcache_error(s.as_str()) {
-            return Err(MemcacheError::from(s));
-        } else if s == "NOT_FOUND\r\n" {
-            return Err(MemcacheError::from(1));
-        } else {
-            match s.trim_end_matches("\r\n").parse::<u64>() {
-                Ok(n) => return Ok(n),
-                Err(_) => return Err(MemcacheError::ClientError("invalid server response".into())),
-            }
-        }
+        let s = MemcacheError::try_from(s)?;
+        Ok(s.trim_end_matches("\r\n").parse::<u64>()?)
     }
 
     pub(super) fn decrement(&mut self, key: &str, amount: u64) -> Result<u64, MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         write!(self.reader.get_mut(), "decr {} {}\r\n", key, amount)?;
         let mut s = String::new();
         self.reader.read_line(&mut s)?;
-        if is_memcache_error(s.as_str()) {
-            return Err(MemcacheError::from(s));
-        } else if s == "NOT_FOUND\r\n" {
-            return Err(MemcacheError::from(1));
-        } else {
-            match s.trim_end_matches("\r\n").parse::<u64>() {
-                Ok(n) => return Ok(n),
-                Err(_) => return Err(MemcacheError::ClientError("invalid server response".into())),
-            }
-        }
+        let s = MemcacheError::try_from(s)?;
+        Ok(s.trim_end_matches("\r\n").parse::<u64>()?)
     }
 
     pub(super) fn touch(&mut self, key: &str, expiration: u32) -> Result<bool, MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         write!(self.reader.get_mut(), "touch {} {}\r\n", key, expiration)?;
         self.reader.get_mut().flush()?;
         let mut s = String::new();
         self.reader.read_line(&mut s)?;
-        if is_memcache_error(s.as_str()) {
-            return Err(MemcacheError::from(s));
-        } else if s == "TOUCHED\r\n" {
-            return Ok(true);
-        } else if s == "NOT_FOUND\r\n" {
-            return Ok(false);
-        } else {
-            return Err(MemcacheError::ClientError(String::from("invalid server response")));
+        match MemcacheError::try_from(s) {
+            Ok(s) => {
+                if s == "TOUCHED\r\n" {
+                    Ok(true)
+                } else {
+                    Err(ServerError::BadResponse(s))?
+                }
+            }
+            Err(MemcacheError::CommandError(CommandError::KeyNotFound)) => Ok(false),
+            Err(e) => Err(e),
         }
     }
 
@@ -401,17 +370,17 @@ impl AsciiProtocol<Stream> {
             let mut s = String::new();
             self.reader.read_line(&mut s)?;
 
-            if is_memcache_error(s.as_str()) {
-                return Err(MemcacheError::from(s));
-            } else if s.starts_with("END") {
+            let s = MemcacheError::try_from(s)?;
+            // FIXME: what if a stat starts with END?
+            if s.starts_with("END") {
                 break;
             } else if !s.starts_with("STAT") {
-                return Err(MemcacheError::ClientError("invalid server response".into()));
+                return Err(ServerError::BadResponse(s).into());
             }
 
             let stat: Vec<_> = s.trim_end_matches("\r\n").split(" ").collect();
             if stat.len() < 3 {
-                return Err(MemcacheError::ClientError("invalid server response".into()));
+                return Err(ServerError::BadResponse(s).into());
             }
             let key = stat[1];
             let value = s.trim_start_matches(format!("STAT {}", key).as_str());
@@ -421,7 +390,3 @@ impl AsciiProtocol<Stream> {
         return Ok(result);
     }
 }
-
-fn is_memcache_error(s: &str) -> bool {
-    return s == "ERROR\r\n" || s.starts_with("CLIENT_ERROR") || s.starts_with("SERVER_ERROR");
-}
diff --git a/src/protocol/binary.rs b/src/protocol/binary.rs
index a0da667..8b2684c 100644
--- a/src/protocol/binary.rs
+++ b/src/protocol/binary.rs
@@ -1,6 +1,7 @@
 use std::collections::HashMap;
 use std::io::Write;
 
+use super::check_key_len;
 use byteorder::{BigEndian, WriteBytesExt};
 use client::Stats;
 use error::MemcacheError;
@@ -27,8 +28,7 @@ impl BinaryProtocol {
         self.stream.write_all(key.as_bytes())?;
         value.write_to(&mut self.stream)?;
         self.stream.flush()?;
-        binary_packet::parse_start_auth_response(&mut self.stream)?;
-        return Ok(());
+        binary_packet::parse_start_auth_response(&mut self.stream).map(|_| ())
     }
 
     fn send_request<V: ToMemcacheValue<Stream>>(
@@ -39,9 +39,7 @@ impl BinaryProtocol {
         expiration: u32,
         cas: Option<u64>,
     ) -> Result<(), MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         let request_header = PacketHeader {
             magic: Magic::Request as u8,
             opcode: opcode as u8,
@@ -72,7 +70,7 @@ impl BinaryProtocol {
         cas: Option<u64>,
     ) -> Result<(), MemcacheError> {
         self.send_request(opcode, key, value, expiration, cas)?;
-        return binary_packet::parse_response(&mut self.stream);
+        binary_packet::parse_response(&mut self.stream)?.err().map(|_| ())
     }
 
     pub(super) fn version(&mut self) -> Result<String, MemcacheError> {
@@ -95,8 +93,7 @@ impl BinaryProtocol {
         };
         request_header.write(&mut self.stream)?;
         self.stream.flush()?;
-        binary_packet::parse_response(&mut self.stream)?;
-        return Ok(());
+        binary_packet::parse_response(&mut self.stream)?.err().map(|_| ())
     }
 
     pub(super) fn flush_with_delay(&mut self, delay: u32) -> Result<(), MemcacheError> {
@@ -110,14 +107,11 @@ impl BinaryProtocol {
         request_header.write(&mut self.stream)?;
         self.stream.write_u32::<BigEndian>(delay)?;
         self.stream.flush()?;
-        binary_packet::parse_response(&mut self.stream)?;
-        return Ok(());
+        binary_packet::parse_response(&mut self.stream)?.err().map(|_| ())
     }
 
     pub(super) fn get<V: FromMemcacheValueExt>(&mut self, key: &str) -> Result<Option<V>, MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         let request_header = PacketHeader {
             magic: Magic::Request as u8,
             opcode: Opcode::Get as u8,
@@ -133,9 +127,7 @@ impl BinaryProtocol {
 
     pub(super) fn gets<V: FromMemcacheValueExt>(&mut self, keys: &[&str]) -> Result<HashMap<String, V>, MemcacheError> {
         for key in keys {
-            if key.len() > 250 {
-                return Err(MemcacheError::ClientError(String::from("key is too long")));
-            }
+            check_key_len(key)?;
             let request_header = PacketHeader {
                 magic: Magic::Request as u8,
                 opcode: Opcode::GetKQ as u8,
@@ -194,9 +186,7 @@ impl BinaryProtocol {
     }
 
     pub(super) fn append<V: ToMemcacheValue<Stream>>(&mut self, key: &str, value: V) -> Result<(), MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         let request_header = PacketHeader {
             magic: Magic::Request as u8,
             opcode: Opcode::Append as u8,
@@ -208,13 +198,11 @@ impl BinaryProtocol {
         self.stream.write_all(key.as_bytes())?;
         value.write_to(&mut self.stream)?;
         self.stream.flush()?;
-        return binary_packet::parse_response(&mut self.stream);
+        binary_packet::parse_response(&mut self.stream)?.err().map(|_| ())
     }
 
     pub(super) fn prepend<V: ToMemcacheValue<Stream>>(&mut self, key: &str, value: V) -> Result<(), MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         let request_header = PacketHeader {
             magic: Magic::Request as u8,
             opcode: Opcode::Prepend as u8,
@@ -226,13 +214,11 @@ impl BinaryProtocol {
         self.stream.write_all(key.as_bytes())?;
         value.write_to(&mut self.stream)?;
         self.stream.flush()?;
-        return binary_packet::parse_response(&mut self.stream);
+        binary_packet::parse_response(&mut self.stream).map(|_| ())
     }
 
     pub(super) fn delete(&mut self, key: &str) -> Result<bool, MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         let request_header = PacketHeader {
             magic: Magic::Request as u8,
             opcode: Opcode::Delete as u8,
@@ -247,9 +233,7 @@ impl BinaryProtocol {
     }
 
     pub(super) fn increment(&mut self, key: &str, amount: u64) -> Result<u64, MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         let request_header = PacketHeader {
             magic: Magic::Request as u8,
             opcode: Opcode::Increment as u8,
@@ -273,9 +257,7 @@ impl BinaryProtocol {
     }
 
     pub(super) fn decrement(&mut self, key: &str, amount: u64) -> Result<u64, MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         let request_header = PacketHeader {
             magic: Magic::Request as u8,
             opcode: Opcode::Decrement as u8,
@@ -299,9 +281,7 @@ impl BinaryProtocol {
     }
 
     pub(super) fn touch(&mut self, key: &str, expiration: u32) -> Result<bool, MemcacheError> {
-        if key.len() > 250 {
-            return Err(MemcacheError::ClientError(String::from("key is too long")));
-        }
+        check_key_len(key)?;
         let request_header = PacketHeader {
             magic: Magic::Request as u8,
             opcode: Opcode::Touch as u8,
diff --git a/src/protocol/binary_packet.rs b/src/protocol/binary_packet.rs
index 97da39a..fe09522 100644
--- a/src/protocol/binary_packet.rs
+++ b/src/protocol/binary_packet.rs
@@ -1,10 +1,11 @@
-use super::ResponseStatus;
 use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
-use error::MemcacheError;
+use error::{CommandError, MemcacheError, ServerError};
 use std::collections::HashMap;
-use std::io;
+use std::io::{self, Cursor};
 use value::FromMemcacheValueExt;
 
+const OK_STATUS: u16 = 0x0;
+
 #[allow(dead_code)]
 pub enum Opcode {
     Get = 0x00,
@@ -73,10 +74,7 @@ impl PacketHeader {
     pub fn read<R: io::Read>(reader: &mut R) -> Result<PacketHeader, MemcacheError> {
         let magic = reader.read_u8()?;
         if magic != Magic::Response as u8 {
-            return Err(MemcacheError::ClientError(format!(
-                "Bad magic number in response header: {}",
-                magic
-            )));
+            return Err(ServerError::BadMagic(magic).into());
         }
         let header = PacketHeader {
             magic,
@@ -93,60 +91,78 @@ impl PacketHeader {
     }
 }
 
-pub fn parse_cas_response<R: io::Read>(stream: &mut R) -> Result<bool, MemcacheError> {
-    let header = PacketHeader::read(stream)?;
-    let status = header.vbucket_id_or_status;
-    // In case of KeyExists and KeyNotFound errors, there is also an error string
-    let value_len = header.total_body_length - u32::from(header.extras_length);
-    let mut err_message = vec![0x0; value_len as usize];
-    stream.read_exact(err_message.as_mut_slice())?;
-    if status == ResponseStatus::KeyExists as u16 || status == ResponseStatus::KeyNotFound as u16 {
-        Ok(false)
-    } else if status == ResponseStatus::NoError as u16 {
-        Ok(true)
-    } else {
-        Err(MemcacheError::from(header.vbucket_id_or_status))
+pub struct Response {
+    header: PacketHeader,
+    key: Vec<u8>,
+    extras: Vec<u8>,
+    value: Vec<u8>,
+}
+
+impl Response {
+    pub(crate) fn err(self) -> Result<Self, MemcacheError> {
+        let status = self.header.vbucket_id_or_status;
+        if status == OK_STATUS {
+            Ok(self)
+        } else {
+            Err(CommandError::from(status))?
+        }
     }
 }
 
-pub fn parse_response<R: io::Read>(reader: &mut R) -> Result<(), MemcacheError> {
+pub fn parse_response<R: io::Read>(reader: &mut R) -> Result<Response, MemcacheError> {
     let header = PacketHeader::read(reader)?;
-    let mut buffer = vec![0; header.total_body_length as usize];
-    reader.read_exact(buffer.as_mut_slice())?;
-    if header.vbucket_id_or_status != ResponseStatus::NoError as u16 {
-        return Err(MemcacheError::from(header.vbucket_id_or_status));
+    let mut extras = vec![0x0; header.extras_length as usize];
+    reader.read_exact(extras.as_mut_slice())?;
+
+    let mut key = vec![0x0; header.key_length as usize];
+    reader.read_exact(key.as_mut_slice())?;
+
+    // TODO: return error if total_body_length < extras_length + key_length
+    let mut value = vec![
+        0x0;
+    (header.total_body_length - u32::from(header.key_length) - u32::from(header.extras_length))
+        as usize
+    ];
+    reader.read_exact(value.as_mut_slice())?;
+
+    Ok(Response {
+        header,
+        key,
+        extras,
+        value,
+    })
+}
+
+pub fn parse_cas_response<R: io::Read>(reader: &mut R) -> Result<bool, MemcacheError> {
+    match parse_response(reader)?.err() {
+        Err(MemcacheError::CommandError(e)) if e == CommandError::KeyNotFound || e == CommandError::KeyExists => {
+            Ok(false)
+        }
+        Ok(_) => Ok(true),
+        Err(e) => Err(e),
     }
-    return Ok(());
 }
 
 pub fn parse_version_response<R: io::Read>(reader: &mut R) -> Result<String, MemcacheError> {
-    let header = PacketHeader::read(reader)?;
-    if header.vbucket_id_or_status != ResponseStatus::NoError as u16 {
-        return Err(MemcacheError::from(header.vbucket_id_or_status));
-    }
-    let mut buffer = vec![0; header.total_body_length as usize];
-    reader.read_exact(buffer.as_mut_slice())?;
-    return Ok(String::from_utf8(buffer)?);
+    let Response { value, .. } = parse_response(reader)?.err()?;
+    Ok(String::from_utf8(value)?)
 }
 
 pub fn parse_get_response<R: io::Read, V: FromMemcacheValueExt>(reader: &mut R) -> Result<Option<V>, MemcacheError> {
-    let header = PacketHeader::read(reader)?;
-    if header.vbucket_id_or_status == ResponseStatus::KeyNotFound as u16 {
-        let mut buffer = vec![0; header.total_body_length as usize];
-        reader.read_exact(buffer.as_mut_slice())?;
-        return Ok(None);
-    } else if header.vbucket_id_or_status != ResponseStatus::NoError as u16 {
-        return Err(MemcacheError::from(header.vbucket_id_or_status));
+    match parse_response(reader)?.err() {
+        Ok(Response {
+            header, extras, value, ..
+        }) => {
+            let flags = Cursor::new(extras).read_u32::<BigEndian>()?;
+            Ok(Some(FromMemcacheValueExt::from_memcache_value(
+                value,
+                flags,
+                Some(header.cas),
+            )?))
+        }
+        Err(MemcacheError::CommandError(CommandError::KeyNotFound)) => Ok(None),
+        Err(e) => Err(e),
     }
-    let flags = reader.read_u32::<BigEndian>()?;
-    let value_length = header.total_body_length - u32::from(header.extras_length);
-    let mut buffer = vec![0; value_length as usize];
-    reader.read_exact(buffer.as_mut_slice())?;
-    return Ok(Some(FromMemcacheValueExt::from_memcache_value(
-        buffer,
-        flags,
-        Some(header.cas),
-    )?));
 }
 
 pub fn parse_gets_response<R: io::Read, V: FromMemcacheValueExt>(
@@ -154,93 +170,60 @@ pub fn parse_gets_response<R: io::Read, V: FromMemcacheValueExt>(
 ) -> Result<HashMap<String, V>, MemcacheError> {
     let mut result = HashMap::new();
     loop {
-        let header = PacketHeader::read(reader)?;
-        if header.vbucket_id_or_status != ResponseStatus::NoError as u16 {
-            return Err(MemcacheError::from(header.vbucket_id_or_status));
-        }
+        let Response {
+            header,
+            key,
+            extras,
+            value,
+        } = parse_response(reader)?.err()?;
         if header.opcode == Opcode::Noop as u8 {
             break;
         }
-        let flags = reader.read_u32::<BigEndian>()?;
-        let key_length = header.key_length;
-        let value_length = header.total_body_length - u32::from(key_length) - u32::from(header.extras_length);
-        let mut key_buffer = vec![0; key_length as usize];
-        reader.read_exact(key_buffer.as_mut_slice())?;
-        let key = String::from_utf8(key_buffer)?;
-        let mut value_buffer = vec![0; value_length as usize];
-        reader.read_exact(value_buffer.as_mut_slice())?;
+        let flags = Cursor::new(extras).read_u32::<BigEndian>()?;
+        let key = String::from_utf8(key)?;
         result.insert(
             key,
-            FromMemcacheValueExt::from_memcache_value(value_buffer, flags, Some(header.cas))?,
+            FromMemcacheValueExt::from_memcache_value(value, flags, Some(header.cas))?,
         );
     }
-    return Ok(result);
+    Ok(result)
 }
 
 pub fn parse_delete_response<R: io::Read>(reader: &mut R) -> Result<bool, MemcacheError> {
-    let header = PacketHeader::read(reader)?;
-    if header.total_body_length != 0 {
-        reader.read_exact(vec![0; header.total_body_length as usize].as_mut_slice())?;
-    }
-    if header.vbucket_id_or_status == ResponseStatus::KeyNotFound as u16 {
-        return Ok(false);
-    } else if header.vbucket_id_or_status != ResponseStatus::NoError as u16 {
-        return Err(MemcacheError::from(header.vbucket_id_or_status));
+    match parse_response(reader)?.err() {
+        Ok(_) => Ok(true),
+        Err(MemcacheError::CommandError(CommandError::KeyNotFound)) => Ok(false),
+        Err(e) => Err(e),
     }
-    return Ok(true);
 }
 
 pub fn parse_counter_response<R: io::Read>(reader: &mut R) -> Result<u64, MemcacheError> {
-    let header = PacketHeader::read(reader)?;
-    if header.vbucket_id_or_status != ResponseStatus::NoError as u16 {
-        return Err(MemcacheError::from(header.vbucket_id_or_status));
-    }
-    return Ok(reader.read_u64::<BigEndian>()?);
+    let Response { value, .. } = parse_response(reader)?.err()?;
+    Ok(Cursor::new(value).read_u64::<BigEndian>()?)
 }
 
 pub fn parse_touch_response<R: io::Read>(reader: &mut R) -> Result<bool, MemcacheError> {
-    let header = PacketHeader::read(reader)?;
-    if header.total_body_length != 0 {
-        reader.read_exact(vec![0; header.total_body_length as usize].as_mut_slice())?;
+    match parse_response(reader)?.err() {
+        Ok(_) => Ok(true),
+        Err(MemcacheError::CommandError(CommandError::KeyNotFound)) => Ok(false),
+        Err(e) => Err(e),
     }
-    if header.vbucket_id_or_status == ResponseStatus::KeyNotFound as u16 {
-        return Ok(false);
-    } else if header.vbucket_id_or_status != ResponseStatus::NoError as u16 {
-        return Err(MemcacheError::from(header.vbucket_id_or_status));
-    }
-    return Ok(true);
 }
 
 pub fn parse_stats_response<R: io::Read>(reader: &mut R) -> Result<HashMap<String, String>, MemcacheError> {
     let mut result = HashMap::new();
     loop {
-        let header = PacketHeader::read(reader)?;
-        if header.vbucket_id_or_status != ResponseStatus::NoError as u16 {
-            return Err(MemcacheError::from(header.vbucket_id_or_status));
-        }
-        let key_length = header.key_length;
-        let value_length = header.total_body_length - u32::from(key_length) - u32::from(header.extras_length);
-        let mut key_buffer = vec![0; key_length as usize];
-        reader.read_exact(key_buffer.as_mut_slice())?;
-        let key = String::from_utf8(key_buffer)?;
-        let mut value_buffer = vec![0; value_length as usize];
-        reader.read_exact(value_buffer.as_mut_slice())?;
-        let value = String::from_utf8(value_buffer)?;
-        if key == "" && value == "" {
+        let Response { key, value, .. } = parse_response(reader)?.err()?;
+        let key = String::from_utf8(key)?;
+        let value = String::from_utf8(value)?;
+        if key.is_empty() && value.is_empty() {
             break;
         }
         result.insert(key, value);
     }
-    return Ok(result);
+    Ok(result)
 }
 
 pub fn parse_start_auth_response<R: io::Read>(reader: &mut R) -> Result<bool, MemcacheError> {
-    let header = PacketHeader::read(reader)?;
-    if header.total_body_length != 0 {
-        reader.read_exact(vec![0; header.total_body_length as usize].as_mut_slice())?;
-    }
-    if header.vbucket_id_or_status != ResponseStatus::NoError as u16 {
-        return Err(MemcacheError::from(header.vbucket_id_or_status));
-    }
-    return Ok(true);
+    parse_response(reader)?.err().map(|_| true)
 }
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
index 0c45a77..56d667b 100644
--- a/src/protocol/mod.rs
+++ b/src/protocol/mod.rs
@@ -2,6 +2,7 @@ mod ascii;
 mod binary;
 mod binary_packet;
 
+use crate::error::ClientError;
 use client::Stats;
 use enum_dispatch::enum_dispatch;
 use error::MemcacheError;
@@ -11,22 +12,19 @@ use std::collections::HashMap;
 use stream::Stream;
 use value::{FromMemcacheValueExt, ToMemcacheValue};
 
+pub(crate) fn check_key_len(key: &str) -> Result<(), MemcacheError> {
+    if key.len() > 250 {
+        Err(ClientError::KeyTooLong)?
+    }
+    Ok(())
+}
+
 #[enum_dispatch]
 pub enum Protocol {
     Ascii(AsciiProtocol<Stream>),
     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>;
diff --git a/src/value.rs b/src/value.rs
index b399ed3..13f6e43 100644
--- a/src/value.rs
+++ b/src/value.rs
@@ -159,10 +159,7 @@ macro_rules! impl_from_memcache_value_for_number {
         impl FromMemcacheValue for $ty {
             fn from_memcache_value(value: Vec<u8>, _: u32) -> MemcacheValue<Self> {
                 let s: String = FromMemcacheValue::from_memcache_value(value, 0)?;
-                match Self::from_str(s.as_str()) {
-                    Ok(v) => return Ok(v),
-                    Err(e) => Err(MemcacheError::from(e)),
-                }
+                Ok(Self::from_str(s.as_str())?)
             }
         }
     };


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index 490288d..2fe8b82 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -59,36 +59,36 @@ fn test() {
 
 #[test]
 fn issue74() {
-    let mut client = memcache::Client::connect("memcache://localhost:12346?tcp_nodelay=true").unwrap();
+    use memcache::{Client, CommandError, MemcacheError};
+    let mut client = Client::connect("memcache://localhost:12346?tcp_nodelay=true").unwrap();
     client.delete("issue74").unwrap();
     client.add("issue74", 1, 0).unwrap();
 
     match client.add("issue74", 1, 0) {
         Ok(_) => panic!("Should got an error!"),
         Err(e) => match e {
-            memcache::MemcacheError::ServerError(e) => assert!(e == 2),
+            MemcacheError::CommandError(e) => assert!(e == CommandError::KeyExists),
             _ => panic!("Unexpected error!"),
-        }
+        },
     }
 
     match client.add("issue74", 1, 0) {
         Ok(_) => panic!("Should got an error!"),
         Err(e) => match e {
-            memcache::MemcacheError::ServerError(e) => assert!(e == 2),
+            MemcacheError::CommandError(e) => assert!(e == CommandError::KeyExists),
             _ => panic!("Unexpected error!"),
-        }
+        },
     }
 
     match client.add("issue74", 1, 0) {
         Ok(_) => panic!("Should got an error!"),
         Err(e) => match e {
-            memcache::MemcacheError::ServerError(e) => assert!(e == 2),
+            MemcacheError::CommandError(e) => assert!(e == CommandError::KeyExists),
             _ => panic!("Unexpected error!"),
-        }
+        },
     }
 }
 
-
 #[test]
 fn udp_test() {
     let urls = vec!["memcache+udp://localhost:22345"];