Repository: elastic-rs/elastic (language: rust)

Issue:
Only include non-400-like errors in ApiError
I don't think there's a lot of value including errors like `ActionRequestValidation` or `Parsing` in `ApiError`. It's really meant to be a structure that you can easily programmatically match on and respond to. But there isn't really much you can do with parsing errors.

We have this distinction between 'errors you can deal with' and `errors you can log and fail with' in `elastic`, so we should do the same here and capture other errors from the API elsewhere.


Discussion / hints:
A bit of a papercut with the API response as it is is that it doesn't capture backtraces like client errors. I want the error to be easily matchable in a result, but this is kind of unfortunate.

Fix patch:
diff --git a/src/elastic/examples/custom_response.rs b/src/elastic/examples/custom_response.rs
index b1261eb181..4bfb91e788 100644
--- a/src/elastic/examples/custom_response.rs
+++ b/src/elastic/examples/custom_response.rs
@@ -35,7 +35,7 @@ struct Hit {
 
 // Implement `IsOk` for our custom `SearchResponse` so it can be used in the call to `into_response`.
 impl IsOk for SearchResponse {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(body)),
             _ => Ok(MaybeOkResponse::err(body)),
diff --git a/src/elastic/src/client/responses/mod.rs b/src/elastic/src/client/responses/mod.rs
index 794bc29f58..573bd3831e 100644
--- a/src/elastic/src/client/responses/mod.rs
+++ b/src/elastic/src/client/responses/mod.rs
@@ -24,8 +24,5 @@ pub use elastic_responses::bulk;
 pub mod prelude {
     /*! A glob import for convenience. */
 
-    pub use super::{BulkErrorsResponse, BulkResponse, CommandResponse, DeleteResponse, GetResponse, IndexResponse, PingResponse, SearchResponse, Shards, UpdateResponse};
-
-    pub use super::async::AsyncResponseBuilder;
-    pub use super::sync::SyncResponseBuilder;
+    pub use super::{AsyncResponseBuilder, SyncResponseBuilder, BulkErrorsResponse, BulkResponse, CommandResponse, DeleteResponse, GetResponse, IndexResponse, PingResponse, SearchResponse, Shards, UpdateResponse};
 }
diff --git a/src/elastic/src/client/responses/parse.rs b/src/elastic/src/client/responses/parse.rs
index 54141ac925..e71cec0fb3 100644
--- a/src/elastic/src/client/responses/parse.rs
+++ b/src/elastic/src/client/responses/parse.rs
@@ -18,7 +18,7 @@ struct MyResponse {
 }
 
 impl IsOk for MyResponse {
-    fn is_ok<B>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError>
+    fn is_ok<B>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError>
         where B: ResponseBody
     {
         match head.status() {
@@ -48,7 +48,7 @@ The `MyResponse` type can then be used for deserialising a concrete response:
 #    took: u64
 # }
 # impl IsOk for MyResponse {
-#     fn is_ok<B>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError>
+#     fn is_ok<B>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError>
 #         where B: ResponseBody
 #     {
 #         match head.status() {
@@ -90,4 +90,4 @@ See the [`IsOk`][IsOk] trait for more details.
 pub(crate) use elastic_responses::parse;
 
 pub use elastic_responses::parsing::{Buffered, HttpResponseHead, IsOk, MaybeBufferedResponse, MaybeOkResponse, ResponseBody, Unbuffered};
-pub use elastic_responses::error::ParseResponseError;
+pub use elastic_responses::error::ParseError;
diff --git a/src/elastic/src/client/sender/sniffed_nodes/nodes_info.rs b/src/elastic/src/client/sender/sniffed_nodes/nodes_info.rs
index caa8b072a4..aa568468d7 100644
--- a/src/elastic/src/client/sender/sniffed_nodes/nodes_info.rs
+++ b/src/elastic/src/client/sender/sniffed_nodes/nodes_info.rs
@@ -2,7 +2,7 @@
 
 use std::fmt;
 use serde::de::{Deserialize, Deserializer, MapAccess, Visitor};
-use client::responses::parse::{HttpResponseHead, IsOk, MaybeOkResponse, ParseResponseError, ResponseBody, Unbuffered};
+use client::responses::parse::{HttpResponseHead, IsOk, MaybeOkResponse, ParseError, ResponseBody, Unbuffered};
 
 use std::iter::IntoIterator;
 use std::vec::IntoIter;
@@ -32,7 +32,7 @@ impl IntoIterator for NodesInfoResponse {
 }
 
 impl IsOk for NodesInfoResponse {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, unbuffered: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, unbuffered: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(unbuffered)),
             _ => Ok(MaybeOkResponse::err(unbuffered)),
diff --git a/src/responses/src/bulk.rs b/src/responses/src/bulk.rs
index 0e4d3383db..d90a74416e 100644
--- a/src/responses/src/bulk.rs
+++ b/src/responses/src/bulk.rs
@@ -521,7 +521,7 @@ pub enum Action {
 }
 
 impl<TIndex, TType, TId> IsOk for BulkResponse<TIndex, TType, TId> {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(body)),
             _ => Ok(MaybeOkResponse::err(body)),
@@ -530,7 +530,7 @@ impl<TIndex, TType, TId> IsOk for BulkResponse<TIndex, TType, TId> {
 }
 
 impl<TIndex, TType, TId> IsOk for BulkErrorsResponse<TIndex, TType, TId> {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(body)),
             _ => Ok(MaybeOkResponse::err(body)),
diff --git a/src/responses/src/command.rs b/src/responses/src/command.rs
index e154258260..9ff653e7f7 100644
--- a/src/responses/src/command.rs
+++ b/src/responses/src/command.rs
@@ -23,7 +23,7 @@ impl CommandResponse {
 }
 
 impl IsOk for CommandResponse {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(body)),
             _ => Ok(MaybeOkResponse::err(body)),
diff --git a/src/responses/src/delete.rs b/src/responses/src/delete.rs
index 13898f4841..e974f13c08 100644
--- a/src/responses/src/delete.rs
+++ b/src/responses/src/delete.rs
@@ -54,7 +54,7 @@ impl DeleteResponse {
 }
 
 impl IsOk for DeleteResponse {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 | 404 => Ok(MaybeOkResponse::ok(body)),
             _ => Ok(MaybeOkResponse::err(body)),
diff --git a/src/responses/src/error.rs b/src/responses/src/error.rs
index ea729ecb6f..80296ae594 100644
--- a/src/responses/src/error.rs
+++ b/src/responses/src/error.rs
@@ -1,24 +1,89 @@
 /*!
-Error types from Elasticsearch
+Error types from Elasticsearch.
 */
 
 use serde::{Deserialize, Deserializer};
 use serde_json::{Error as JsonError, Map, Value};
+use std::error::Error as StdError;
 use std::io::Error as IoError;
+use std::fmt;
+
+mod inner {
+    use std::fmt;
+    use std::error::Error as StdError;
+    use serde_json::{Map, Value};
+
+    use super::ApiError;
 
-quick_error! {
-    /** An error parsing a response stream. */
     #[derive(Debug)]
-    pub enum ParseResponseError {
-        /** The response contains invalid json. */
-        Json(err: JsonError) {
-            from()
+    pub struct UnknownApiError(pub Map<String, Value>);
+
+    impl fmt::Display for UnknownApiError {
+        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+            (&self.0 as &fmt::Debug).fmt(f)
         }
-        /** The response caused an io error while buffering. */
-        Io(err: IoError) {
-            from()
+    }
+
+    impl StdError for UnknownApiError {
+        fn description(&self) -> &str {
+            "an unknown API error"
+        }
+
+        fn cause(&self) -> Option<&StdError> {
+            None
         }
     }
+
+    pub enum ParsedApiError {
+        Known(ApiError),
+        Unknown(Map<String, Value>)
+    }
+}
+
+pub(crate) use self::inner::*;
+
+/**
+A generic error parsing an API response.
+*/
+#[derive(Debug)]
+pub struct ParseError {
+    inner: Box<StdError + Send + Sync>,
+}
+
+impl ParseError {
+    pub fn new<E>(err: E) -> Self where E: StdError + Send + Sync + 'static {
+        ParseError {
+            inner: Box::new(err)
+        }
+    }
+}
+
+impl From<IoError> for ParseError {
+    fn from(err: IoError) -> Self {
+        ParseError::new(err)
+    }
+}
+
+impl From<JsonError> for ParseError {
+    fn from(err: JsonError) -> Self {
+        ParseError::new(err)
+    }
+}
+
+impl fmt::Display for ParseError {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.inner.fmt(f)
+    }
+}
+
+impl StdError for ParseError {
+    fn description(&self) -> &str {
+        self.inner.description()
+    }
+
+    fn cause(&self) -> Option<&StdError> {
+        Some(&*self.inner)
+    }
 }
 
 quick_error! {
@@ -30,7 +95,7 @@ quick_error! {
             from()
         }
         /** An error parsing a response body. */
-        Parse(err: ParseResponseError) {
+        Parse(err: ParseError) {
             from()
         }
     }
@@ -69,24 +134,6 @@ quick_error! {
             display("index already exists: '{}'", index)
         }
         /**
-        The request body contains invalid data.
-
-        If a Query DSL query contains invalid JSON or unrecognised properties then Elasticsearch will return a `Parsing` error.
-        */
-        Parsing { line: u64, col: u64, reason: String } {
-            description("request parse error")
-            display("request parse error: '{}' on line: {}, col: {}", reason, line, col)
-        }
-        /**
-        The document mapping contains invalid data.
-
-        If a put mapping request contains invalid JSON or unrecognised properties then Elasticsearch will return a `MapperParsing` error.
-        */
-        MapperParsing { reason: String } {
-            description("mapper parse error")
-            display("mapper parse error: '{}'", reason)
-        }
-        /**
         The request body can't be processed.
 
         Some endpoints that expect certain constraints of a request to hold will return an `ActionRequestValidation` error if those constraints aren't met.
@@ -95,17 +142,6 @@ quick_error! {
             description("action request failed validation")
             display("action request failed validation: '{}'", reason)
         }
-        /**
-        A currently unrecognised error occurred.
-
-        **WARNING:** Don't rely on this variant to capture an error.
-        As new variants are introduced they will no longer be matched by `ApiError::Other`.
-        For this reason, this variant will disappear in the future.
-        */
-        Other(v: Map<String, Value>) {
-            description("error response from Elasticsearch")
-            display("error response from Elasticsearch: {:?}", v)
-        }
         #[doc(hidden)]
         __NonExhaustive {}
     }
@@ -119,13 +155,13 @@ macro_rules! error_key {
 
             match key {
                 Some(v) => v,
-                _ => return ApiError::Other($obj).into()
+                _ => return ParsedApiError::Unknown($obj)
             }
         }
     )
 }
 
-impl<'de> Deserialize<'de> for ApiError {
+impl<'de> Deserialize<'de> for ParsedApiError {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
         D: Deserializer<'de>,
@@ -136,12 +172,12 @@ impl<'de> Deserialize<'de> for ApiError {
     }
 }
 
-impl From<Map<String, Value>> for ApiError {
+impl From<Map<String, Value>> for ParsedApiError {
     fn from(mut value: Map<String, Value>) -> Self {
         let obj = {
             match value.remove("error") {
                 Some(Value::Object(value)) => value,
-                _ => return ApiError::Other(value),
+                _ => return ParsedApiError::Unknown(value),
             }
         };
 
@@ -152,7 +188,7 @@ impl From<Map<String, Value>> for ApiError {
 
             match ty {
                 Some(ty) => ty,
-                _ => return ApiError::Other(obj),
+                _ => return ParsedApiError::Unknown(obj),
             }
         };
 
@@ -160,50 +196,32 @@ impl From<Map<String, Value>> for ApiError {
             "index_not_found_exception" => {
                 let index = error_key!(obj[index]: |v| v.as_str());
 
-                ApiError::IndexNotFound {
+                ParsedApiError::Known(ApiError::IndexNotFound {
                     index: index.into(),
-                }
+                })
             }
             "index_already_exists_exception" => {
                 let index = error_key!(obj[index]: |v| v.as_str());
 
-                ApiError::IndexAlreadyExists {
+                ParsedApiError::Known(ApiError::IndexAlreadyExists {
                     index: index.into(),
-                }
+                })
             }
             "document_missing_exception" => {
                 let index = error_key!(obj[index]: |v| v.as_str());
 
-                ApiError::DocumentMissing {
+                ParsedApiError::Known(ApiError::DocumentMissing {
                     index: index.into(),
-                }
-            }
-            "parsing_exception" => {
-                let line = error_key!(obj[line]: |v| v.as_u64());
-                let col = error_key!(obj[col]: |v| v.as_u64());
-                let reason = error_key!(obj[reason]: |v| v.as_str());
-
-                ApiError::Parsing {
-                    line: line,
-                    col: col,
-                    reason: reason.into(),
-                }
-            }
-            "mapper_parsing_exception" => {
-                let reason = error_key!(obj[reason]: |v| v.as_str());
-
-                ApiError::MapperParsing {
-                    reason: reason.into(),
-                }
+                })
             }
             "action_request_validation_exception" => {
                 let reason = error_key!(obj[reason]: |v| v.as_str());
 
-                ApiError::ActionRequestValidation {
+                ParsedApiError::Known(ApiError::ActionRequestValidation {
                     reason: reason.into(),
-                }
+                })
             }
-            _ => ApiError::Other(obj),
+            _ => ParsedApiError::Unknown(obj),
         }
     }
 }
diff --git a/src/responses/src/get.rs b/src/responses/src/get.rs
index 55aa335117..989505da0f 100644
--- a/src/responses/src/get.rs
+++ b/src/responses/src/get.rs
@@ -57,7 +57,7 @@ impl<T> GetResponse<T> {
 }
 
 impl<T: DeserializeOwned> IsOk for GetResponse<T> {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(body)),
             404 => {
diff --git a/src/responses/src/index.rs b/src/responses/src/index.rs
index 8d266aa1d4..7306c34d1a 100644
--- a/src/responses/src/index.rs
+++ b/src/responses/src/index.rs
@@ -50,7 +50,7 @@ impl IndexResponse {
 }
 
 impl IsOk for IndexResponse {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(body)),
             _ => Ok(MaybeOkResponse::err(body)),
diff --git a/src/responses/src/parsing.rs b/src/responses/src/parsing.rs
index 051b86fd8b..38b4ad65c7 100644
--- a/src/responses/src/parsing.rs
+++ b/src/responses/src/parsing.rs
@@ -135,13 +135,13 @@ where
     type Buffered: ResponseBody;
 
     /** Buffer the response body to a json value and return a new buffered representation. */
-    fn body(self) -> Result<(Value, Self::Buffered), ParseResponseError>;
+    fn body(self) -> Result<(Value, Self::Buffered), ParseError>;
 
     /** Parse the body as a success result. */
-    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseResponseError>;
+    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseError>;
 
     /** Parse the body as an API error. */
-    fn parse_err(self) -> Result<ApiError, ParseResponseError>;
+    fn parse_err(self) -> Result<ApiError, ParseError>;
 }
 
 struct ReadBody<B>(B);
@@ -149,7 +149,7 @@ struct ReadBody<B>(B);
 impl<B: Read> ResponseBody for ReadBody<B> {
     type Buffered = SliceBody<Vec<u8>>;
 
-    fn body(mut self) -> Result<(Value, Self::Buffered), ParseResponseError> {
+    fn body(mut self) -> Result<(Value, Self::Buffered), ParseError> {
         let mut buf = Vec::new();
         self.0.read_to_end(&mut buf)?;
 
@@ -158,12 +158,15 @@ impl<B: Read> ResponseBody for ReadBody<B> {
         Ok((body, SliceBody(buf)))
     }
 
-    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseResponseError> {
+    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseError> {
         serde_json::from_reader(self.0).map_err(|e| e.into())
     }
 
-    fn parse_err(self) -> Result<ApiError, ParseResponseError> {
-        serde_json::from_reader(self.0).map_err(|e| e.into())
+    fn parse_err(self) -> Result<ApiError, ParseError> {
+        match serde_json::from_reader(self.0)? {
+            ParsedApiError::Known(err) => Ok(err),
+            ParsedApiError::Unknown(err) => Err(ParseError::new(UnknownApiError(err)))
+        }
     }
 }
 
@@ -172,7 +175,7 @@ struct SliceBody<B>(B);
 impl<B: AsRef<[u8]>> ResponseBody for SliceBody<B> {
     type Buffered = Self;
 
-    fn body(self) -> Result<(Value, Self::Buffered), ParseResponseError> {
+    fn body(self) -> Result<(Value, Self::Buffered), ParseError> {
         let buf = self.0;
 
         let body: Value = serde_json::from_slice(buf.as_ref())?;
@@ -180,12 +183,15 @@ impl<B: AsRef<[u8]>> ResponseBody for SliceBody<B> {
         Ok((body, SliceBody(buf)))
     }
 
-    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseResponseError> {
+    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseError> {
         serde_json::from_slice(self.0.as_ref()).map_err(|e| e.into())
     }
 
-    fn parse_err(self) -> Result<ApiError, ParseResponseError> {
-        serde_json::from_slice(self.0.as_ref()).map_err(|e| e.into())
+    fn parse_err(self) -> Result<ApiError, ParseError> {
+        match serde_json::from_slice(self.0.as_ref())? {
+            ParsedApiError::Known(err) => Ok(err),
+            ParsedApiError::Unknown(err) => Err(ParseError::new(UnknownApiError(err)))
+        }
     }
 }
 
@@ -223,7 +229,7 @@ Implement `IsOk` for a custom response type, where a http `404` might still cont
 # #[derive(Deserialize)]
 # struct MyResponse;
 impl IsOk for MyResponse {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, unbuffered: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, unbuffered: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(unbuffered)),
             404 => {
@@ -245,11 +251,11 @@ impl IsOk for MyResponse {
 */
 pub trait IsOk {
     /** Inspect the http response to determine whether or not it succeeded. */
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, unbuffered: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError>;
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, unbuffered: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError>;
 }
 
 impl IsOk for Value {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(body)),
             _ => Ok(MaybeOkResponse::err(body)),
@@ -306,7 +312,7 @@ pub struct Unbuffered<B>(B);
 
 impl<B: ResponseBody> Unbuffered<B> {
     /** Buffer the response body to a json value and return a new buffered representation. */
-    pub fn body(self) -> Result<(Value, Buffered<B>), ParseResponseError> {
+    pub fn body(self) -> Result<(Value, Buffered<B>), ParseError> {
         self.0.body().map(|(value, body)| (value, Buffered(body)))
     }
 }
@@ -332,14 +338,14 @@ impl<B> MaybeBufferedResponse<B>
 where
     B: ResponseBody,
 {
-    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseResponseError> {
+    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseError> {
         match self {
             MaybeBufferedResponse::Unbuffered(b) => b.parse_ok(),
             MaybeBufferedResponse::Buffered(b) => b.parse_ok(),
         }
     }
 
-    fn parse_err(self) -> Result<ApiError, ParseResponseError> {
+    fn parse_err(self) -> Result<ApiError, ParseError> {
         match self {
             MaybeBufferedResponse::Unbuffered(b) => b.parse_err(),
             MaybeBufferedResponse::Buffered(b) => b.parse_err(),
diff --git a/src/responses/src/ping.rs b/src/responses/src/ping.rs
index d0d62b718f..b25cf0d428 100644
--- a/src/responses/src/ping.rs
+++ b/src/responses/src/ping.rs
@@ -69,7 +69,7 @@ impl ClusterVersion {
 }
 
 impl IsOk for PingResponse {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(body)),
             _ => Ok(MaybeOkResponse::err(body)),
diff --git a/src/responses/src/search.rs b/src/responses/src/search.rs
index c082437ac5..e8122bde2c 100644
--- a/src/responses/src/search.rs
+++ b/src/responses/src/search.rs
@@ -136,7 +136,7 @@ impl<T> SearchResponse<T> {
 }
 
 impl<T: DeserializeOwned> IsOk for SearchResponse<T> {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(body)),
             _ => Ok(MaybeOkResponse::err(body)),
diff --git a/src/responses/src/update.rs b/src/responses/src/update.rs
index f7068aef41..6c279854a3 100644
--- a/src/responses/src/update.rs
+++ b/src/responses/src/update.rs
@@ -48,7 +48,7 @@ impl UpdateResponse {
 }
 
 impl IsOk for UpdateResponse {
-    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
+    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseError> {
         match head.status() {
             200...299 => Ok(MaybeOkResponse::ok(body)),
             _ => Ok(MaybeOkResponse::err(body)),
diff --git a/src/types/src/date/formats.rs b/src/types/src/date/formats.rs
index 35672b39f0..f99827e983 100644
--- a/src/types/src/date/formats.rs
+++ b/src/types/src/date/formats.rs
@@ -78,7 +78,7 @@ impl DateFormat for EpochMillis {
 #[cfg(test)]
 mod tests {
     use chrono::{DateTime, Utc};
-    use date::{format, parse};
+    use date::{format, parse, ParseError};
     use prelude::*;
 
     #[test]
diff --git a/src/types/src/date/mod.rs b/src/types/src/date/mod.rs
index 0cae8abf8f..1f64cf67c4 100644
--- a/src/types/src/date/mod.rs
+++ b/src/types/src/date/mod.rs
@@ -93,6 +93,7 @@ You can also manually implement `DateFormat` and write your own arbitrary format
 # extern crate elastic_types;
 # extern crate chrono;
 # use elastic_types::prelude::*;
+# use elastic_types::date::ParseError;
 # fn main() {
 use chrono::{DateTime, Utc};
 
@@ -135,7 +136,7 @@ pub mod prelude {
     */
 
     pub use super::DefaultDateFormat;
-    pub use super::format::*;
+    pub use super::format::{DateFormat, DateValue, FormattableDateValue, FormattedDate};
     pub use super::impls::*;
     pub use super::formats::*;
     pub use super::mapping::*;


Test patch:
diff --git a/src/responses/tests/index/mod.rs b/src/responses/tests/index/mod.rs
index b732e157d6..8fd292ccf7 100644
--- a/src/responses/tests/index/mod.rs
+++ b/src/responses/tests/index/mod.rs
@@ -17,19 +17,6 @@ fn success_parse_response() {
     assert_eq!(Some(1), deserialized.version());
 }
 
-#[test]
-fn error_parse_mapping() {
-    let f = load_file("tests/samples/error_mapper_parsing.json");
-    let deserialized = parse::<IndexResponse>().from_reader(400, f).unwrap_err();
-
-    let valid = match deserialized {
-        ResponseError::Api(ApiError::MapperParsing { ref reason }) if reason == "failed to parse, document is empty" => true,
-        _ => false,
-    };
-
-    assert!(valid);
-}
-
 #[test]
 fn error_parse_index_already_exists() {
     let f = load_file("tests/samples/error_index_already_exists.json");
diff --git a/src/responses/tests/search/mod.rs b/src/responses/tests/search/mod.rs
index b8670325b7..e6bed1d384 100644
--- a/src/responses/tests/search/mod.rs
+++ b/src/responses/tests/search/mod.rs
@@ -207,45 +207,3 @@ fn error_parse_index_not_found() {
 
     assert!(valid);
 }
-
-#[test]
-fn error_parse_parsing() {
-    let f = load_file("tests/samples/error_parsing.json");
-    let deserialized = parse::<SearchResponse<Value>>()
-        .from_reader(400, f)
-        .unwrap_err();
-
-    let valid = match deserialized {
-        ResponseError::Api(ApiError::Parsing {
-            line: 2,
-            col: 9,
-            ref reason,
-        }) if reason == "Unknown key for a START_OBJECT in [qry]." =>
-        {
-            true
-        }
-        _ => false,
-    };
-
-    assert!(valid);
-}
-
-#[test]
-fn error_parse_other() {
-    let f = load_file("tests/samples/error_other.json");
-    let deserialized = parse::<SearchResponse<Value>>()
-        .from_reader(500, f)
-        .unwrap_err();
-
-    let reason = match deserialized {
-        ResponseError::Api(ApiError::Other(ref err)) => err.get("reason")
-            .and_then(|reason| reason.as_str())
-            .map(|reason| reason.to_owned()),
-        _ => None,
-    };
-
-    assert_eq!(
-        Some(String::from("An error that we don't know about.")),
-        reason
-    );
-}