1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//! Response type parsing.

use serde::de::DeserializeOwned;
use serde_json::{self, Value};

use std::io::{Cursor, Read};

use super::{HttpResponse, HttpResponseHead, SliceBody, ReadBody};
use error::*;

impl<B: ResponseBody> HttpResponse<B> {
    /// Convert this http response into either `Ok(T)` or an `Err(ApiError)`.
    /// 
    /// # Examples
    /// 
    /// Any type that implements `IsOk` can be parsed into a concrete response or an `ApiError`:
    /// 
    /// ```no_run
    /// # extern crate serde_json;
    /// # extern crate elastic_responses;
    /// # use serde_json::*;
    /// # use elastic_responses::*;
    /// # use elastic_responses::error::*;
    /// # fn do_request() -> HttpResponseSlice<Vec<u8>> { unimplemented!() }
    /// # fn main() {
    /// // Send a document get request and read as a generic HttpResponse
    /// let http_response = do_request();
    ///
    /// let get_response = http_response.into_response::<GetResponseOf<Value>>();
    /// 
    /// match get_response {
    ///     Ok(res) => {
    ///         // Do something with the GetResponse
    ///     }
    ///     Err(ResponseError::Api(ApiError::IndexNotFound { index })) => {
    ///         // Do something with the missing index error
    ///     }
    ///     _ => {
    ///         // Some other error
    ///     }
    /// }
    /// # }
    /// ```
    pub fn into_response<T: IsOk + DeserializeOwned>(self) -> Result<T, ResponseError> {
        let maybe = T::is_ok(self.head, Unbuffered(self.body))?;

        match maybe.ok {
            true => {
                let ok = maybe.res.parse_ok()?;
                Ok(ok)
            }
            false => {
                let err = maybe.res.parse_err()?;
                Err(ResponseError::Api(err))
            }
        }
    }
}

/// A http response body that can be buffered into a json value.
pub trait ResponseBody where Self: Sized
{
    /// The type of a buffered response body.
    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>;

    /// Parse the body as a success result.
    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseResponseError>;

    /// Parse the body as an API error.
    fn parse_err(self) -> Result<ApiError, ParseResponseError>;
}

impl<B: Read> ResponseBody for ReadBody<B> {
    type Buffered = SliceBody<Vec<u8>>;

    fn body(mut self) -> Result<(Value, Self::Buffered), ParseResponseError> {
        let mut buf = Vec::new();
        self.0.read_to_end(&mut buf)?;

        let body: Value = serde_json::from_reader(Cursor::new(&buf))?;

        Ok((body, SliceBody(buf)))
    }

    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseResponseError> {
        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())
    }
}

impl<B: AsRef<[u8]>> ResponseBody for SliceBody<B> {
    type Buffered = Self;

    fn body(self) -> Result<(Value, Self::Buffered), ParseResponseError> {
        let buf = self.0;

        let body: Value = serde_json::from_slice(buf.as_ref())?;

        Ok((body, SliceBody(buf)))
    }

    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseResponseError> {
        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())
    }
}

/// Convert a response message into a either a success
/// or failure result.
pub trait IsOk
{
    /// Inspect the http response to determine whether or not it succeeded.
    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError>;
}

impl IsOk for Value {
    fn is_ok<B: ResponseBody>(head: HttpResponseHead, body: Unbuffered<B>) -> Result<MaybeOkResponse<B>, ParseResponseError> {
        match head.status() {
            200...299 => Ok(MaybeOkResponse::ok(body)),
            _ => Ok(MaybeOkResponse::err(body)),
        }
    }
}

/// A response that might be successful or an `ApiError`.
pub struct MaybeOkResponse<B> 
    where B: ResponseBody
{
    ok: bool,
    res: MaybeBufferedResponse<B>,
}

impl<B> MaybeOkResponse<B> where B: ResponseBody
{
    /// Create a new response that indicates where or not the
    /// body is successful or an `ApiError`.
    pub fn new<I>(ok: bool, res: I) -> Self
        where I: Into<MaybeBufferedResponse<B>>
    {
        MaybeOkResponse {
            ok: ok,
            res: res.into(),
        }
    }

    /// Create a response where the body is successful.
    pub fn ok<I>(res: I) -> Self
        where I: Into<MaybeBufferedResponse<B>>
    {
        Self::new(true, res)
    }

    /// Create a resposne where the body is an error.
    pub fn err<I>(res: I) -> Self
        where I: Into<MaybeBufferedResponse<B>>
    {
        Self::new(false, res)
    }
}

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> {
        self.0.body().map(|(value, body)| (value, Buffered(body)))
    }
}

pub struct Buffered<B: ResponseBody>(B::Buffered);

/// A response body that may or may not have been buffered.
///
/// This type makes it possible to inspect the response body for
/// an error type before passing it along to be deserialised properly.
pub enum MaybeBufferedResponse<B>
    where B: ResponseBody
{
    Unbuffered(B),
    Buffered(B::Buffered),
}

impl<B> MaybeBufferedResponse<B>
    where B: ResponseBody
{
    fn parse_ok<T: DeserializeOwned>(self) -> Result<T, ParseResponseError> {
        match self {
            MaybeBufferedResponse::Unbuffered(b) => b.parse_ok(),
            MaybeBufferedResponse::Buffered(b) => b.parse_ok()
        }
    }

    fn parse_err(self) -> Result<ApiError, ParseResponseError> {
        match self {
            MaybeBufferedResponse::Unbuffered(b) => b.parse_err(),
            MaybeBufferedResponse::Buffered(b) => b.parse_err()
        }
    }
}

impl<B> From<Unbuffered<B>> for MaybeBufferedResponse<B>
    where B: ResponseBody
{
    fn from(value: Unbuffered<B>) -> Self {
        MaybeBufferedResponse::Unbuffered(value.0)
    }
}

impl<B> From<Buffered<B>> for MaybeBufferedResponse<B>
    where B: ResponseBody
{
    fn from(value: Buffered<B>) -> Self {
        MaybeBufferedResponse::Buffered(value.0)
    }
}