use std::borrow::Cow;
use std::collections::HashMap;
use serde::Serialize;
use serde::de::DeserializeOwned;
use http::{Request, Response, Method};
use futures::future::Either;
use crate::errors::{MatrixError, MatrixResult};
use types::replies::BadRequestReply;
use serde_json;
use percent_encoding::{utf8_percent_encode, DEFAULT_ENCODE_SET};
use futures::{self, Future, Poll, Async, try_ready};
use std::marker::PhantomData;
pub trait ApiType {
fn get_path<'a>(&'a self) -> Cow<'a, str>;
}
pub mod apis {
pub mod r0 {
use crate::request::ApiType;
use std::borrow::Cow;
pub struct ClientApi;
impl ApiType for ClientApi {
fn get_path(&self) -> Cow<'static, str> {
"/_matrix/client/r0".into()
}
}
pub struct MediaApi;
impl ApiType for MediaApi {
fn get_path(&self) -> Cow<'static, str> {
"/_matrix/media/r0".into()
}
}
}
}
pub struct TypedApiResponse<T, U, V> {
response: Option<U>,
body: Option<V>,
parts: Option<::http::response::Parts>,
_ph: PhantomData<T>,
discard: bool
}
impl<T, U, RB, V> Future for TypedApiResponse<T, U, V>
where T: DeserializeOwned + 'static,
RB: AsRef<[u8]>,
V: Future<Item = RB, Error = MatrixError>,
U: Future<Item = Response<V>, Error = MatrixError> {
type Item = T;
type Error = MatrixError;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
use std::any::TypeId;
use std::ptr;
if self.response.is_some() {
let resp = try_ready!(self.response.as_mut().unwrap().poll());
self.response = None;
let (parts, body) = resp.into_parts();
self.parts = Some(parts);
self.body = Some(body);
}
let body = try_ready!(self.body.as_mut().unwrap().poll());
let parts = self.parts.take().unwrap();
if !parts.status.is_success() {
if let Ok(e) = ::serde_json::from_slice::<BadRequestReply>(body.as_ref()) {
return Err(MatrixError::BadRequest(e));
}
else {
return Err(MatrixError::HttpCode(parts.status));
}
}
let data = if TypeId::of::<T>() == TypeId::of::<()>() && self.discard {
unsafe {
ptr::read(&() as *const () as *const T)
}
}
else {
::serde_json::from_slice::<T>(body.as_ref())?
};
Ok(Async::Ready(data))
}
}
pub trait MatrixRequestable {
type Txnid: ::std::fmt::Display;
type ResponseBody: AsRef<[u8]>;
type ResponseBodyFuture: Future<Item = Self::ResponseBody, Error = MatrixError> + 'static;
type SendRequestFuture: Future<Item = Response<Self::ResponseBodyFuture>, Error = MatrixError> + 'static;
fn get_url(&self) -> Cow<str>;
fn is_as(&self) -> bool { false }
fn get_access_token(&self) -> Cow<str>;
fn get_user_id(&self) -> Cow<str>;
fn get_txnid(&mut self) -> Self::Txnid;
fn send_request(&mut self, req: Request<Vec<u8>>) -> Self::SendRequestFuture;
fn typed_api_call<T>(&mut self, req: Request<Vec<u8>>, discard: bool) -> TypedApiResponse<T, Self::SendRequestFuture, Self::ResponseBodyFuture> where T: DeserializeOwned + 'static {
TypedApiResponse {
response: Some(self.send_request(req)),
body: None,
parts: None,
discard,
_ph: PhantomData
}
}
}
use self::apis::r0::*;
pub struct MatrixRequest<'a, T, U = ClientApi> {
pub meth: Method,
pub endpoint: Cow<'a, str>,
pub params: HashMap<Cow<'a, str>, Cow<'a, str>>,
pub body: T,
pub typ: U
}
impl<'a, T, U> MatrixRequest<'a, T, U> where T: Serialize, U: ApiType {
pub fn new<S: Into<Cow<'a, str>>>(meth: Method, endpoint: S, body: T, typ: U) -> Self {
Self {
meth,
endpoint: endpoint.into(),
params: HashMap::new(),
body,
typ
}
}
}
impl<'a> MatrixRequest<'a, ()> {
pub fn new_basic<S: Into<Cow<'a, str>>>(meth: Method, endpoint: S) -> Self {
Self {
meth,
endpoint: endpoint.into(),
params: HashMap::new(),
body: (),
typ: ClientApi
}
}
}
impl<'a, 'b, 'c> MatrixRequest<'a, HashMap<Cow<'b, str>, Cow<'c, str>>> {
pub fn new_with_body<S, T, U, V>(meth: Method, endpoint: S, body: V) -> Self
where S: Into<Cow<'a, str>>,
T: Into<Cow<'b, str>>,
U: Into<Cow<'c, str>>,
V: IntoIterator<Item=(T, U)> {
let body = body.into_iter().map(|(t, u)| (t.into(), u.into()))
.collect();
Self {
meth,
endpoint: endpoint.into(),
params: HashMap::new(),
body,
typ: ClientApi
}
}
}
impl<'a, T> MatrixRequest<'a, T> where T: Serialize {
pub fn new_with_body_ser<S>(meth: Method, endpoint: S, body: T) -> Self
where S: Into<Cow<'a, str>> {
Self {
meth,
endpoint: endpoint.into(),
params: HashMap::new(),
body,
typ: ClientApi
}
}
}
impl<'a, T, U> MatrixRequest<'a, T, U> where T: Serialize, U: ApiType {
fn body(&self) -> MatrixResult<Vec<u8>> {
let body = serde_json::to_string(&self.body)?;
Ok(if body == "{}" {
vec![]
}
else {
body.into_bytes()
})
}
pub fn make_request<C>(&self, client: &C) -> MatrixResult<Request<Vec<u8>>> where C: MatrixRequestable {
let body = self.body()?;
let mut params = format!("access_token={}", client.get_access_token());
if client.is_as() {
params += &format!("&user_id={}",
utf8_percent_encode(&client.get_user_id(), DEFAULT_ENCODE_SET));
}
for (k, v) in self.params.iter() {
params += &format!("&{}={}",
utf8_percent_encode(k.as_ref(), DEFAULT_ENCODE_SET),
utf8_percent_encode(v.as_ref(), DEFAULT_ENCODE_SET));
}
let url = format!("{}{}{}?{}",
client.get_url(),
self.typ.get_path(),
self.endpoint,
params);
let req = Request::builder()
.method(self.meth.clone())
.uri(url)
.body(body)?;
Ok(req)
}
pub fn send<C, R>(&self, mxc: &mut C) -> impl Future<Item = R, Error = MatrixError> + 'static where R: DeserializeOwned + 'static, C: MatrixRequestable {
let req = match self.make_request(mxc) {
Ok(r) => r,
Err(e) => return Either::B(futures::future::err(e.into()))
};
Either::A(mxc.typed_api_call(req, false))
}
pub fn discarding_send<'x, 'y, C>(&'x self, mxc: &'y mut C) -> impl Future<Item = (), Error = MatrixError> + 'static where C: MatrixRequestable {
let req = match self.make_request(mxc) {
Ok(r) => r,
Err(e) => return Either::B(futures::future::err(e.into()))
};
Either::A(mxc.typed_api_call(req, true))
}
}