1use std::borrow::Cow;
4use std::collections::HashMap;
5use serde::Serialize;
6use serde::de::DeserializeOwned;
7use http::{Request, Response, Method};
8use futures::future::Either;
9use crate::errors::{MatrixError, MatrixResult};
10use types::replies::BadRequestReply;
11use serde_json;
12use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
13use futures::{self, Future, Poll, Async, try_ready};
14use std::marker::PhantomData;
15
16pub trait ApiType {
18 fn get_path<'a>(&'a self) -> Cow<'a, str>;
23}
24pub mod apis {
26 pub mod r0 {
28 use crate::request::ApiType;
29 use std::borrow::Cow;
30 pub struct ClientApi;
32 impl ApiType for ClientApi {
33 fn get_path(&self) -> Cow<'static, str> {
34 "/_matrix/client/r0".into()
35 }
36 }
37 pub struct MediaApi;
39 impl ApiType for MediaApi {
40 fn get_path(&self) -> Cow<'static, str> {
41 "/_matrix/media/r0".into()
42 }
43 }
44 }
45}
46pub struct TypedApiResponse<T, U, V> {
50 response: Option<U>,
51 body: Option<V>,
52 parts: Option<::http::response::Parts>,
53 _ph: PhantomData<T>,
54 discard: bool
55}
56impl<T, U, RB, V> Future for TypedApiResponse<T, U, V>
57 where T: DeserializeOwned + 'static,
58 RB: AsRef<[u8]>,
59 V: Future<Item = RB, Error = MatrixError>,
60 U: Future<Item = Response<V>, Error = MatrixError> {
61
62 type Item = T;
63 type Error = MatrixError;
64
65 fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
66 use std::any::TypeId;
67 use std::ptr;
68
69 if self.response.is_some() {
70 let resp = try_ready!(self.response.as_mut().unwrap().poll());
71 self.response = None;
72 let (parts, body) = resp.into_parts();
73 self.parts = Some(parts);
74 self.body = Some(body);
75 }
76 let body = try_ready!(self.body.as_mut().unwrap().poll());
77 let parts = self.parts.take().unwrap();
78 if !parts.status.is_success() {
79 if let Ok(e) = ::serde_json::from_slice::<BadRequestReply>(body.as_ref()) {
80 return Err(MatrixError::BadRequest(e));
81 }
82 else {
83 return Err(MatrixError::HttpCode(parts.status));
84 }
85 }
86 let data = if TypeId::of::<T>() == TypeId::of::<()>() && self.discard {
87 unsafe {
91 ptr::read(&() as *const () as *const T)
92 }
93 }
94 else {
95 ::serde_json::from_slice::<T>(body.as_ref())?
96 };
97 Ok(Async::Ready(data))
98 }
99}
100pub trait MatrixRequestable {
102 type Txnid: ::std::fmt::Display;
104 type ResponseBody: AsRef<[u8]>;
106 type ResponseBodyFuture: Future<Item = Self::ResponseBody, Error = MatrixError> + 'static;
110 type SendRequestFuture: Future<Item = Response<Self::ResponseBodyFuture>, Error = MatrixError> + 'static;
114 fn get_url(&self) -> Cow<str>;
116 fn is_as(&self) -> bool { false }
118 fn get_access_token(&self) -> Cow<str>;
120 fn get_user_id(&self) -> Cow<str>;
122 fn get_txnid(&mut self) -> Self::Txnid;
127 fn send_request(&mut self, req: Request<Vec<u8>>) -> Self::SendRequestFuture;
129
130 fn typed_api_call<T>(&mut self, req: Request<Vec<u8>>, discard: bool) -> TypedApiResponse<T, Self::SendRequestFuture, Self::ResponseBodyFuture> where T: DeserializeOwned + 'static {
136 TypedApiResponse {
137 response: Some(self.send_request(req)),
138 body: None,
139 parts: None,
140 discard,
141 _ph: PhantomData
142 }
143 }
144}
145
146use self::apis::r0::*;
147pub struct MatrixRequest<'a, T, U = ClientApi> {
157 pub meth: Method,
159 pub endpoint: Cow<'a, str>,
161 pub params: HashMap<Cow<'a, str>, Cow<'a, str>>,
163 pub body: T,
168 pub typ: U
170}
171impl<'a, T, U> MatrixRequest<'a, T, U> where T: Serialize, U: ApiType {
172 pub fn new<S: Into<Cow<'a, str>>>(meth: Method, endpoint: S, body: T, typ: U) -> Self {
174 Self {
175 meth,
176 endpoint: endpoint.into(),
177 params: HashMap::new(),
178 body,
179 typ
180 }
181 }
182}
183impl<'a> MatrixRequest<'a, ()> {
184 pub fn new_basic<S: Into<Cow<'a, str>>>(meth: Method, endpoint: S) -> Self {
191 Self {
192 meth,
193 endpoint: endpoint.into(),
194 params: HashMap::new(),
195 body: (),
196 typ: ClientApi
197 }
198 }
199}
200impl<'a, 'b, 'c> MatrixRequest<'a, HashMap<Cow<'b, str>, Cow<'c, str>>> {
201 pub fn new_with_body<S, T, U, V>(meth: Method, endpoint: S, body: V) -> Self
208 where S: Into<Cow<'a, str>>,
209 T: Into<Cow<'b, str>>,
210 U: Into<Cow<'c, str>>,
211 V: IntoIterator<Item=(T, U)> {
212 let body = body.into_iter().map(|(t, u)| (t.into(), u.into()))
213 .collect();
214 Self {
215 meth,
216 endpoint: endpoint.into(),
217 params: HashMap::new(),
218 body,
219 typ: ClientApi
220 }
221 }
222}
223impl<'a, T> MatrixRequest<'a, T> where T: Serialize {
224 pub fn new_with_body_ser<S>(meth: Method, endpoint: S, body: T) -> Self
226 where S: Into<Cow<'a, str>> {
227 Self {
228 meth,
229 endpoint: endpoint.into(),
230 params: HashMap::new(),
231 body,
232 typ: ClientApi
233 }
234 }
235}
236impl<'a, T, U> MatrixRequest<'a, T, U> where T: Serialize, U: ApiType {
237 fn body(&self) -> MatrixResult<Vec<u8>> {
238 let body = serde_json::to_string(&self.body)?;
239 Ok(if body == "{}" {
240 vec![]
241 }
242 else {
243 body.into_bytes()
244 })
245 }
246 pub fn make_request<C>(&self, client: &C) -> MatrixResult<Request<Vec<u8>>> where C: MatrixRequestable {
248 let body = self.body()?;
249 let mut params = format!("access_token={}", client.get_access_token());
250 if client.is_as() {
251 params += &format!("&user_id={}",
252 utf8_percent_encode(&client.get_user_id(), DEFAULT_ENCODE_SET));
253 }
254 for (k, v) in self.params.iter() {
255 params += &format!("&{}={}",
256 utf8_percent_encode(k.as_ref(), DEFAULT_ENCODE_SET),
257 utf8_percent_encode(v.as_ref(), DEFAULT_ENCODE_SET));
258 }
259 let url = format!("{}{}{}?{}",
260 client.get_url(),
261 self.typ.get_path(),
262 self.endpoint,
263 params);
264 let req = Request::builder()
265 .method(self.meth.clone())
266 .uri(url)
267 .body(body)?;
268 Ok(req)
269 }
270 pub fn send<C, R>(&self, mxc: &mut C) -> impl Future<Item = R, Error = MatrixError> + 'static where R: DeserializeOwned + 'static, C: MatrixRequestable {
275 let req = match self.make_request(mxc) {
276 Ok(r) => r,
277 Err(e) => return Either::B(futures::future::err(e.into()))
278 };
279 Either::A(mxc.typed_api_call(req, false))
280 }
281 pub fn discarding_send<'x, 'y, C>(&'x self, mxc: &'y mut C) -> impl Future<Item = (), Error = MatrixError> + 'static where C: MatrixRequestable {
283 let req = match self.make_request(mxc) {
284 Ok(r) => r,
285 Err(e) => return Either::B(futures::future::err(e.into()))
286 };
287 Either::A(mxc.typed_api_call(req, true))
288 }
289}