Revise implementation of Client::request to use the latest ruma-api/ruma-client-api.

This commit is contained in:
Jimmy Cuadra 2017-05-11 23:36:34 -07:00
parent bccad7e40c
commit fafe30fdec
4 changed files with 51 additions and 197 deletions

View File

@ -11,16 +11,21 @@ repository = "https://github.com/ruma/ruma-client"
version = "0.1.0"
[dependencies]
futures = "0.1.7"
ruma-identifiers = "0.6.0"
serde = "0.8.21"
serde_json = "0.8.4"
serde_urlencoded = "0.3.0"
tokio-core = "0.1.3"
url = "1.2.4"
futures = "0.1.13"
ruma-identifiers = "0.11.0"
serde = "1.0.2"
serde_json = "1.0.1"
serde_urlencoded = "0.4.3"
tokio-core = "0.1.6"
url = "1.4.0"
[dependencies.hyper]
git = "https://github.com/hyperium/hyper"
rev = "fed04dfb58e19b408322d4e5ca7474871e848a35"
[dependencies.ruma-api]
git = "https://github.com/ruma/ruma-api"
[dependencies.ruma-client-api]
git = "https://github.com/ruma/ruma-client-api"
branch = "manual"

View File

@ -1,4 +1,5 @@
use hyper::Error as HyperError;
use ruma_client_api::Error as RumaClientApiError;
use serde_json::Error as SerdeJsonError;
use serde_urlencoded::ser::Error as SerdeUrlEncodedSerializeError;
use url::ParseError;
@ -10,6 +11,8 @@ pub enum Error {
Hyper(HyperError),
/// An error when parsing a string as a URL.
Url(ParseError),
/// An error converting between ruma_client_api types and Hyper types.
RumaClientApi(RumaClientApiError),
/// An error when serializing or deserializing a JSON value.
SerdeJson(SerdeJsonError),
/// An error when serializing a query string value.
@ -30,6 +33,12 @@ impl From<ParseError> for Error {
}
}
impl From<RumaClientApiError> for Error {
fn from(error: RumaClientApiError) -> Error {
Error::RumaClientApi(error)
}
}
impl From<SerdeJsonError> for Error {
fn from(error: SerdeJsonError) -> Error {
Error::SerdeJson(error)

View File

@ -2,10 +2,12 @@
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![feature(conservative_impl_trait, try_from)]
extern crate futures;
extern crate hyper;
extern crate ruma_client_api;
extern crate ruma_api;
pub extern crate ruma_client_api;
extern crate ruma_identifiers;
extern crate serde;
extern crate serde_json;
@ -13,22 +15,19 @@ extern crate serde_urlencoded;
extern crate tokio_core;
extern crate url;
use std::borrow::Cow;
use std::fmt::Debug;
use std::convert::{TryFrom, TryInto};
use hyper::client::{Client as HyperClient, HttpConnector, Request as HyperRequest};
use hyper::Method as HyperMethod;
use ruma_client_api::{Endpoint, Method};
use ruma_client_api::unversioned::get_supported_versions;
use futures::{Future, IntoFuture};
use hyper::{Client as HyperClient, Request as HyperRequest, Response as HyperResponse};
use hyper::client::HttpConnector;
use ruma_api::Endpoint;
use tokio_core::reactor::Handle;
use url::Url;
pub use error::Error;
pub use session::Session;
pub use response::{FutureResponse, Response};
mod error;
mod response;
mod session;
/// A client for the Matrix client-server API.
@ -39,103 +38,34 @@ pub struct Client {
session: Option<Session>,
}
trait IntoHyperMethod {
fn into_hyper(self) -> HyperMethod;
}
impl IntoHyperMethod for Method {
fn into_hyper(self) -> HyperMethod {
match self {
Method::Delete => HyperMethod::Delete,
Method::Get => HyperMethod::Get,
Method::Put => HyperMethod::Put,
Method::Post => HyperMethod::Post,
}
}
}
impl Client {
/// Creates a new client for making requests to the given homeserver.
///
/// # Errors
///
/// Returns an error if the given homeserver URL cannot be parsed as a URL.
pub fn new<U>(handle: &Handle, homeserver_url: U) -> Result<Self, Error> where U: TryIntoUrl {
Ok(Client {
homeserver_url: homeserver_url.try_into()?,
pub fn new(handle: &Handle, homeserver_url: Url) -> Self {
Client {
homeserver_url,
hyper: HyperClient::configure().keep_alive(true).build(handle),
session: None,
})
}
/// Get the versions of the Matrix client-server specification supported by the homeserver.
pub fn get_supported_versions(&mut self)
-> Result<FutureResponse<<get_supported_versions::Endpoint as Endpoint>::Response>, Error> {
self.request::<get_supported_versions::Endpoint>(None, None, None)
}
fn request<E>(
&mut self,
body_params: Option<E::BodyParams>,
path_params: Option<E::PathParams>,
query_params: Option<E::QueryParams>,
) -> Result<FutureResponse<E::Response>, Error>
where E: Endpoint, <E as Endpoint>::Response: Debug + Send {
let path = match path_params {
Some(params) => Cow::from(E::request_path(params)),
None => Cow::from(E::router_path()),
};
let mut url = self.homeserver_url.join(path.as_ref())?.try_into()?;
if let Some(params) = query_params {
url.set_query(Some(&serde_urlencoded::to_string(&params)?));
}
}
if E::requires_authentication() {
if let Some(ref session) = self.session {
url.query_pairs_mut().append_pair("access_token", &session.access_token);
} else {
return Err(Error::AuthenticationRequired);
}
}
/// Makes a request to a Matrix API endpoint.
pub fn request<E>(&self, request: <E as Endpoint>::Request)
-> impl Future<Item = <E as Endpoint>::Response, Error = Error>
where E: Endpoint,
<E as Endpoint>::Response: 'static,
Error: From<<<E as Endpoint>::Request as TryInto<HyperRequest>>::Error>,
Error: From<<<E as Endpoint>::Response as TryFrom<HyperResponse>>::Error> {
let cloned_hyper = self.hyper.clone();
let mut request = HyperRequest::new(E::method().into_hyper(), url);
match E::method() {
Method::Post | Method::Put => {
if let Some(params) = body_params {
request.set_body(serde_json::to_string(&params)?);
}
}
_ => {}
}
Ok(FutureResponse::from(self.hyper.request(request)))
}
}
/// Functionally equivalent to `TryInto<Url>`, and should be replaced by that as soon as it's
/// stable and available.
pub trait TryIntoUrl {
/// Performs the conversion.
fn try_into(self) -> Result<Url, Error>;
}
impl TryIntoUrl for String {
fn try_into(self) -> Result<Url, Error> {
Url::parse(&self).map_err(Error::from)
}
}
impl<'a> TryIntoUrl for &'a str {
fn try_into(self) -> Result<Url, Error> {
Url::parse(self).map_err(Error::from)
}
}
impl TryIntoUrl for Url {
fn try_into(self) -> Result<Url, Error> {
Ok(self)
request
.try_into()
.map_err(Error::from)
.into_future()
.and_then(move |hyper_request| {
cloned_hyper.request(hyper_request).map_err(Error::from)
})
.and_then(|hyper_response| {
hyper_response.try_into().map_err(Error::from)
})
}
}

View File

@ -1,90 +0,0 @@
use std::fmt::Debug;
use std::io::Write;
use std::marker::PhantomData;
use futures::{Async, Future, Poll, Stream};
use hyper::client::{FutureResponse as HyperFutureResponse, Response as HyperResponse};
use hyper::Error as HyperError;
use hyper::header::Headers;
use hyper::status::StatusCode;
use hyper::HttpVersion;
use serde::Deserialize;
use serde_json::from_slice;
use Error;
/// A `Future` that will resolve into a `Response`.
#[derive(Debug)]
pub struct FutureResponse<T> where T: Debug + Deserialize + Send + 'static {
hyper_future_response: HyperFutureResponse,
phantom: PhantomData<T>,
}
/// A response from a Matrix homeserver.
#[derive(Debug)]
pub struct Response<T> where T: Debug + Deserialize {
/// The Hyper response.
hyper_response: HyperResponse,
/// The response from the Matrix API.
phantom: PhantomData<T>,
}
impl<T> Future for FutureResponse<T> where T: Debug + Deserialize + Send + 'static {
type Item = Response<T>;
type Error = Error;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
match self.hyper_future_response.poll() {
Ok(Async::Ready(hyper_response)) => Ok(Async::Ready(Response {
hyper_response: hyper_response,
phantom: PhantomData,
})),
Ok(Async::NotReady) => Ok(Async::NotReady),
Err(error) => Err(Error::from(error)),
}
}
}
impl<T> From<HyperFutureResponse> for FutureResponse<T>
where T: Debug + Deserialize + Send + 'static {
fn from(hyper_future_response: HyperFutureResponse) -> FutureResponse<T> {
FutureResponse {
hyper_future_response: hyper_future_response,
phantom: PhantomData,
}
}
}
impl<T> Response<T> where T: Debug + Deserialize + Send + 'static {
/// The response from the Matrix API.
pub fn data(self) -> Box<Future<Item=T, Error=Error>> {
let bytes = self.hyper_response.body().fold(Vec::new(), |mut bytes, chunk| {
if let Err(error) = bytes.write_all(&chunk) {
return Err(HyperError::from(error));
}
Ok(bytes)
}).map_err(Error::from);
let deserialized_data = bytes.and_then(|bytes| {
from_slice(bytes.as_slice()).map_err(Error::from)
});
deserialized_data.boxed()
}
/// The HTTP response code.
pub fn status(&self) -> &StatusCode {
self.hyper_response.status()
}
/// The HTTP response headers.
pub fn headers(&self) -> &Headers {
self.hyper_response.headers()
}
/// The HTTP version.
pub fn http_version(&self) -> &HttpVersion {
self.hyper_response.version()
}
}