1
1
2
2
pub mod client \{
3
- use futures::\{Future, future};
4
- use futures::stream::Stream;
3
+ use failure::Fail;
4
+ use futures::\{Stream, Future};
5
+ use futures_preview::compat::Future01CompatExt;
5
6
use parking_lot::Mutex;
6
- use reqwest::r#async::\{Decoder, Response};
7
- use serde::de::DeserializeOwned ;
7
+
8
+ use std::fmt::Debug ;
8
9
9
10
/// Common API errors.
10
11
#[derive(Debug, Fail)]
11
- pub enum ApiError \{
12
+ pub enum ApiError< R: Debug + Send + ' static> \{
12
13
#[fail(display = "API request failed for path: \{} (code: \{})", _0, _1)]
13
- Failure(String, reqwest:: StatusCode, Mutex<Response >),
14
+ Failure(String, http::status:: StatusCode, Mutex<R >),
14
15
#[fail(display = "Unsupported media type in response: \{}", _0)]
15
- UnsupportedMediaType(String, Mutex<Response >),
16
+ UnsupportedMediaType(String, Mutex<R >),
16
17
#[fail(display = "An error has occurred while performing the API request: \{}", _0)]
17
18
Reqwest(reqwest::Error),
18
19
{{ - for coder in media_coders }}
@@ -21,39 +22,130 @@ pub mod client \{
21
22
{{ - endfor }}
22
23
}
23
24
25
+ /// HTTP Request.
26
+ pub trait Request \{
27
+ /// Sets the header with the given key and value.
28
+ fn header(self, name: &' static str, value: &str) -> Self;
29
+
30
+ /// Sets body using the given vector of bytes.
31
+ ///
32
+ /// **NOTE:** Appropriate `Content-Type` header must be set
33
+ /// after calling this method.
34
+ fn body_bytes(self, body: Vec<u8 >) -> Self;
35
+
36
+ /// Sets JSON body based on the given value.
37
+ fn json<T: serde::Serialize>(self, value: & T) -> Self;
38
+
39
+ /// Sets/adds query parameters based on the given value.
40
+ ///
41
+ /// **NOTE:** This method must be called only once. It's unspecified
42
+ /// as to whether this appends/replaces query parameters.
43
+ fn query<T: serde::Serialize>(self, params: & T) -> Self;
44
+ }
45
+
46
+ impl Request for reqwest::r#async::RequestBuilder \{
47
+ fn header(self, name: & 'static str, value: & str) -> Self \{
48
+ reqwest::r#async::RequestBuilder::header(self, name, value)
49
+ }
50
+
51
+ fn body_bytes(self, body: Vec<u8 >) -> Self \{
52
+ self.body(body)
53
+ }
54
+
55
+ fn json<T: serde::Serialize>(self, value: & T) -> Self \{
56
+ reqwest::r#async::RequestBuilder::json(self, value)
57
+ }
58
+
59
+ fn query<T: serde::Serialize>(self, params: & T) -> Self \{
60
+ reqwest::r#async::RequestBuilder::query(self, params)
61
+ }
62
+ }
63
+
64
+ /// HTTP Response.
65
+ #[async_trait::async_trait]
66
+ pub trait Response: Debug + Send + Sized \{
67
+ type Bytes: AsRef< [u8]>;
68
+
69
+ /// Gets the value for the given header name, if any.
70
+ fn header(& self, name: & 'static str) -> Option< & str>;
71
+
72
+ /// Status code for this response.
73
+ fn status(& self) -> http::status::StatusCode;
74
+
75
+ /// Media type for this response body (if any).
76
+ fn media_type(& self) -> Option<mime::MediaType >;
77
+
78
+ /// Vector of bytes from the response body.
79
+ async fn body_bytes(self) -> Result< (Self, Self::Bytes), ApiError<Self >>;
80
+ }
81
+
82
+ #[async_trait::async_trait]
83
+ impl Response for reqwest::r#async::Response \{
84
+ type Bytes = reqwest::r#async::Chunk;
85
+
86
+ fn header(& self, name: & 'static str) -> Option< & str> \{
87
+ self.headers().get(name).and_then(|v| v.to_str().ok())
88
+ }
89
+
90
+ fn status(& self) -> http::status::StatusCode \{
91
+ reqwest::r#async::Response::status(self)
92
+ }
93
+
94
+ fn media_type(& self) -> Option<mime::MediaType > \{
95
+ self.header(http::header::CONTENT_TYPE.as_str())
96
+ .and_then(|v| v.parse().ok())
97
+ }
98
+
99
+ async fn body_bytes(mut self) -> Result< (Self, Self::Bytes), ApiError<Self >> \{
100
+ let body = std::mem::replace(self.body_mut(), reqwest::r#async::Decoder::empty());
101
+ let bytes = body.concat2().map_err(ApiError::Reqwest).compat().await?;
102
+ Ok((self, bytes))
103
+ }
104
+ }
105
+
24
106
/// Represents an API client.
107
+ #[async_trait::async_trait]
25
108
pub trait ApiClient \{
109
+ type Request: Request + Send;
110
+ type Response: Response;
111
+
26
112
/// Consumes a method and a relative path and produces a request builder for a single API call.
27
- fn request_builder(& self, method: reqwest ::Method, rel_path: & str) -> reqwest::r#async::RequestBuilder ;
113
+ fn request_builder(& self, method: http ::Method, rel_path: & str) -> Self::Request ;
28
114
29
115
/// Performs the HTTP request using the given `Request` object
30
116
/// and returns a `Response` future.
31
- fn make_request(& self, req: reqwest::r#async::Request)
32
- -> Box<dyn Future<Item = Response, Error = reqwest::Error> + Send>;
117
+ async fn make_request(& self, req: Self::Request) -> Result<Self::Response , ApiError<Self::Response>>;
33
118
}
34
119
120
+ #[async_trait::async_trait]
35
121
impl ApiClient for reqwest::r#async::Client \{
36
- #[inline]
37
- fn request_builder(& self, method: reqwest::Method, rel_path: & str) -> reqwest::r#async::RequestBuilder \{
122
+ type Request = reqwest::r#async::RequestBuilder;
123
+ type Response = reqwest::r#async::Response;
124
+
125
+ fn request_builder(& self, method: http::Method, rel_path: & str) -> Self::Request \{
38
126
let mut u = String::from("{base_url | unescaped}");
39
127
u.push_str(rel_path.trim_start_matches('/'));
40
128
self.request(method, & u)
41
129
}
42
130
43
- #[inline]
44
- fn make_request( & self, req: reqwest::r#async::Request)
45
- -> Box< dyn Future< Item = Response, Error = reqwest::Error> + Send> \{
46
- Box::new(self.execute(req)) as Box < _>
131
+ async fn make_request( & self, req: Self::Request) -> Result< Self::Response , ApiError<Self::Response>> \{
132
+ let req = req.build().map_err(ApiError::Reqwest)?;
133
+ let resp = self.execute(req).map_err(ApiError::Reqwest).compat().await?;
134
+ Ok(resp)
47
135
}
48
136
}
49
137
50
138
/// A trait for indicating that the implementor can send an API call.
51
- pub trait Sendable \{
139
+ #[async_trait::async_trait]
140
+ pub trait Sendable<Client >
141
+ where
142
+ Client: ApiClient + Sync + 'static,
143
+ \{
52
144
/// The output object from this API request.
53
- type Output: DeserializeOwned + Send + 'static ;
145
+ type Output: serde::de:: DeserializeOwned;
54
146
55
147
/// HTTP method used by this call.
56
- const METHOD: reqwest ::Method;
148
+ const METHOD: http ::Method;
57
149
58
150
/// Relative URL for this API call formatted appropriately with parameter values.
59
151
///
@@ -62,55 +154,41 @@ pub mod client \{
62
154
63
155
/// Modifier for this object. Builders override this method if they
64
156
/// wish to add query parameters, set body, etc.
65
- fn modify(& self, req: reqwest::r#async::RequestBuilder ) -> Result<reqwest::r #async::RequestBuilder , ApiError> \{
157
+ fn modify(& self, req: Client::Request ) -> Result<Client::Request , ApiError<Client::Response> > \{
66
158
Ok(req)
67
159
}
68
160
69
161
/// Sends the request and returns a future for the response object.
70
- fn send(& self, client: & dyn ApiClient) -> Box<dyn Future<Item = Self::Output, Error = ApiError> + Send> \{
71
- Box::new(self.send_raw(client).and_then(|mut resp| -> Box<dyn Future<Item = _, Error = ApiError> + Send> \{
72
- let value = resp.headers().get(reqwest::header::CONTENT_TYPE);
73
- let body_concat = |resp: & mut Response| \{
74
- let body = std::mem::replace(resp.body_mut(), Decoder::empty());
75
- body.concat2().map_err(ApiError::from)
76
- };
77
-
78
- if let Some(ty) = value.as_ref()
79
- .and_then(|v| v.to_str().ok())
80
- .and_then(|v| v.parse::<mime::MediaType >().ok())
81
- \{
82
- {{ - for coder in media_coders }}
83
- {{ if not @first }} else {{ endif }} if media_types::M_{ @index }.matches(& ty) \{
84
- return Box::new(body_concat(& mut resp).and_then(|v| \{
85
- {coder.decoder | unescaped}(v.as_ref()).map_err(ApiError::from)
86
- })) as Box< _>
87
- }
88
- {{ - endfor }}
162
+ async fn send(& self, client: & Client) -> Result<Self::Output , ApiError<Client::Response>> \{
163
+ let resp = self.send_raw(client).await?;
164
+ let media = resp.media_type();
165
+ if let Some(ty) = media \{
166
+ if media_types::M_0.matches(& ty) \{
167
+ let (_, bytes) = resp.body_bytes().await?;
168
+ return serde_json::from_reader(bytes.as_ref()).map_err(ApiError::from)
89
169
}
170
+ else if media_types::M_1.matches(& ty) \{
171
+ let (_, bytes) = resp.body_bytes().await?;
172
+ return serde_yaml::from_reader(bytes.as_ref()).map_err(ApiError::from)
173
+ }
174
+ }
90
175
91
- let ty = value
92
- .map(|v| String::from_utf8_lossy(v.as_bytes()).into_owned())
93
- .unwrap_or_default();
94
- Box::new(futures::future::err(ApiError::UnsupportedMediaType(ty, Mutex::new(resp)))) as Box< _>
95
- })) as Box< _>
176
+ let ty = resp.header(http::header::CONTENT_TYPE.as_str())
177
+ .map(|v| String::from_utf8_lossy(v.as_bytes()).into_owned())
178
+ .unwrap_or_default();
179
+ Err(ApiError::UnsupportedMediaType(ty, Mutex::new(resp)))
96
180
}
97
181
98
182
/// Convenience method for returning a raw response after sending a request.
99
- fn send_raw(& self, client: & dyn ApiClient ) -> Box< dyn Future< Item = Response, Error = ApiError> + Send > \{
183
+ async fn send_raw(& self, client: & Client ) -> Result< Client:: Response , ApiError<Client::Response> > \{
100
184
let rel_path = self.rel_path();
101
- let builder = self.modify(client.request_builder(Self::METHOD, & rel_path));
102
- let req = match builder.and_then(|b| b.build().map_err(ApiError::Reqwest)) \{
103
- Ok(r) => r,
104
- Err(e) => return Box::new(future::err(e)),
105
- };
106
-
107
- Box::new(client.make_request(req).map_err(ApiError::Reqwest).and_then(move |resp| \{
108
- if resp.status().is_success() \{
109
- futures::future::ok(resp)
110
- } else \{
111
- futures::future::err(ApiError::Failure(rel_path.into_owned(), resp.status(), Mutex::new(resp)).into())
112
- }
113
- })) as Box< _>
185
+ let req = self.modify(client.request_builder(Self::METHOD, & rel_path))?;
186
+ let resp = client.make_request(req).await?;
187
+ if resp.status().is_success() \{
188
+ Ok(resp)
189
+ } else \{
190
+ Err(ApiError::Failure(rel_path.into_owned(), resp.status(), Mutex::new(resp)))
191
+ }
114
192
}
115
193
}
116
194
@@ -124,15 +202,9 @@ pub mod client \{
124
202
{{ - endfor }}
125
203
}
126
204
}
127
-
128
- impl From<reqwest::Error > for ApiError \{
129
- fn from(e: reqwest::Error) -> Self \{
130
- ApiError::Reqwest(e)
131
- }
132
- }
133
205
{{ - for coder in media_coders }}
134
206
135
- impl From<{coder .error_ty_path | unescaped}> for ApiError \{
207
+ impl< R: Response + ' static> From<{coder.error_ty_path | unescaped}> for ApiError<R> \{
136
208
fn from(e: {coder.error_ty_path | unescaped}) -> Self \{
137
209
ApiError::{coder.error_variant | unescaped}(e)
138
210
}
0 commit comments