Remove Client lifetime from Futures by using Rc and clones

This commit is contained in:
Jonas Platte 2017-07-12 03:25:00 +10:00
parent ceca663d69
commit 3dae62ec25
3 changed files with 85 additions and 51 deletions

View File

@ -12,30 +12,51 @@ extern crate url;
use std::convert::TryFrom;
use futures::Future;
use hyper::client::Connect;
use ruma_client::Client;
use ruma_client::api::r0;
use ruma_events::EventType;
use ruma_events::room::message::{MessageEventContent, MessageType, TextMessageEventContent};
use ruma_identifiers::RoomAliasId;
use tokio_core::reactor::Core;
use tokio_core::reactor::{Core as TokioCore, Handle as TokioHandle};
use url::Url;
fn hello_world<'a, C: Connect>(client: &'a Client<C>)
-> impl Future<Item = (), Error = ruma_client::Error> + 'a
{
client.register_guest().and_then(move |_| {
r0::alias::get_alias::call(client, r0::alias::get_alias::Request {
// from https://stackoverflow.com/a/43992218/1592377
#[macro_export]
macro_rules! clone {
(@param _) => ( _ );
(@param $x:ident) => ( $x );
($($n:ident),+ => move || $body:expr) => (
{
$( let $n = $n.clone(); )+
move || $body
}
);
($($n:ident),+ => move |$($p:tt),+| $body:expr) => (
{
$( let $n = $n.clone(); )+
move |$(clone!(@param $p),)+| $body
}
);
}
fn hello_world(
tokio_handle: TokioHandle,
homeserver_url: Url,
) -> impl Future<Item = (), Error = ruma_client::Error> + 'static {
let client = Client::https(&tokio_handle, homeserver_url, None).unwrap();
client.register_guest().and_then(clone!(client => move |_| {
r0::alias::get_alias::call(client.clone(), r0::alias::get_alias::Request {
room_alias: RoomAliasId::try_from("#ruma-client-test:matrix.org").unwrap(),
})
}).and_then(move |response| {
})).and_then(clone!(client => move |response| {
let room_id = response.room_id;
r0::membership::join_room_by_id::call(client, r0::membership::join_room_by_id::Request {
r0::membership::join_room_by_id::call(client.clone(), r0::membership::join_room_by_id::Request {
room_id: room_id.clone(),
third_party_signed: None,
}).and_then(move |_| {
r0::send::send_message_event::call(client, r0::send::send_message_event::Request {
}).and_then(clone!(client => move |_| {
r0::send::send_message_event::call(client.clone(), r0::send::send_message_event::Request {
room_id: room_id,
event_type: EventType::RoomMessage,
txn_id: "1".to_owned(),
@ -44,15 +65,14 @@ fn hello_world<'a, C: Connect>(client: &'a Client<C>)
msgtype: MessageType::Text,
}),
})
})
}).map(|_| ())
}))
})).map(|_| ())
}
fn main() {
let mut core = Core::new().unwrap();
let mut core = TokioCore::new().unwrap();
let handle = core.handle();
let server = Url::parse("https://matrix.org/").unwrap();
let client = Client::https(&handle, server, None).unwrap();
core.run(hello_world(&client)).unwrap();
core.run(hello_world(handle, server)).unwrap();
}

View File

@ -32,10 +32,10 @@ macro_rules! endpoint {
use {Client, Error};
/// Make a request to this API endpoint.
pub fn call<'a, C>(
client: &'a Client<C>,
pub fn call<C>(
client: Client<C>,
request: Request,
) -> impl Future<Item = Response, Error = Error> + 'a
) -> impl Future<Item = Response, Error = Error>
where
C: Connect,
{

View File

@ -45,23 +45,23 @@ mod error;
mod session;
/// A client for the Matrix client-server API.
#[derive(Clone, Debug)]
#[derive(Debug)]
pub struct Client<C>
where
C: Connect,
{
homeserver_url: Url,
homeserver_url: Rc<Url>,
hyper: Rc<HyperClient<C>>,
session: RefCell<Option<Session>>,
session: Rc<RefCell<Option<Session>>>,
}
impl Client<HttpConnector> {
/// Creates a new client for making HTTP requests to the given homeserver.
pub fn new(handle: &Handle, homeserver_url: Url, session: Option<Session>) -> Self {
Client {
homeserver_url,
homeserver_url: Rc::new(homeserver_url),
hyper: Rc::new(HyperClient::configure().keep_alive(true).build(handle)),
session: RefCell::new(session),
session: Rc::new(RefCell::new(session)),
}
}
}
@ -73,14 +73,14 @@ impl Client<HttpsConnector<HttpConnector>> {
let connector = HttpsConnector::new(4, handle)?;
Ok(Client {
homeserver_url,
homeserver_url: Rc::new(homeserver_url),
hyper: Rc::new(
HyperClient::configure()
.connector(connector)
.keep_alive(true)
.build(handle),
),
session: RefCell::new(session),
session: Rc::new(RefCell::new(session)),
})
}
}
@ -94,9 +94,9 @@ where
/// This allows the user to configure the details of HTTP as desired.
pub fn custom(hyper_client: HyperClient<C>, homeserver_url: Url, session: Option<Session>) -> Self {
Client {
homeserver_url,
homeserver_url: Rc::new(homeserver_url),
hyper: Rc::new(hyper_client),
session: RefCell::new(session),
session: Rc::new(RefCell::new(session)),
}
}
@ -105,8 +105,8 @@ where
/// In contrast to api::r0::session::login::call(), this method stores the
/// session data returned by the endpoint in this client, instead of
/// returning it.
pub fn log_in<'a>(&'a self, user: String, password: String)
-> impl Future<Item = (), Error = Error> + 'a {
pub fn log_in(&self, user: String, password: String)
-> impl Future<Item = (), Error = Error> {
let request = login::Request {
address: None,
login_type: login::LoginType::Password,
@ -115,8 +115,10 @@ where
user,
};
login::call(self, request).and_then(move |response| {
*self.session.borrow_mut() = Some(Session::new(response.access_token, response.user_id));
let session = self.session.clone();
login::call(self.clone(), request).and_then(move |response| {
*session.borrow_mut() = Some(Session::new(response.access_token, response.user_id));
Ok(())
})
@ -125,10 +127,12 @@ where
/// Register as a guest. In contrast to api::r0::account::register::call(),
/// this method stores the session data returned by the endpoint in this
/// client, instead of returning it.
pub fn register_guest<'a>(&'a self) -> impl Future<Item = (), Error = Error> + 'a {
pub fn register_guest(&self) -> impl Future<Item = (), Error = Error> {
use api::r0::account::register;
register::call(self, register::Request {
let session = self.session.clone();
register::call(self.clone(), register::Request {
auth: None,
bind_email: None,
device_id: None,
@ -137,8 +141,7 @@ where
password: None,
username: None,
}).map(move |response| {
*self.session.borrow_mut() =
Some(Session::new(response.access_token, response.user_id));
*session.borrow_mut() = Some(Session::new(response.access_token, response.user_id));
})
}
@ -150,14 +153,16 @@ where
///
/// The username is the local part of the returned user_id. If it is
/// omitted from this request, the server will generate one.
pub fn register_user<'a>(
&'a self,
pub fn register_user(
&self,
username: Option<String>,
password: String,
) -> impl Future<Item = (), Error = Error> + 'a {
) -> impl Future<Item = (), Error = Error> {
use api::r0::account::register;
register::call(self, register::Request {
let session = self.session.clone();
register::call(self.clone(), register::Request {
auth: None,
bind_email: None,
device_id: None,
@ -166,21 +171,21 @@ where
password: Some(password),
username: username,
}).map(move |response| {
*self.session.borrow_mut() =
Some(Session::new(response.access_token, response.user_id));
*session.borrow_mut() = Some(Session::new(response.access_token, response.user_id));
})
}
/// Makes a request to a Matrix API endpoint.
pub(crate) fn request<'a, E>(
&'a self,
pub(crate) fn request<E>(
self,
request: <E as Endpoint>::Request,
) -> impl Future<Item = E::Response, Error = Error> + 'a
) -> impl Future<Item = E::Response, Error = Error>
where
E: Endpoint,
<E as Endpoint>::Response: 'a,
{
let mut url = self.homeserver_url.clone();
let mut url = (*self.homeserver_url).clone();
let hyper = self.hyper;
let session = self.session;
request
.try_into()
@ -194,7 +199,7 @@ where
url.set_query(uri.query());
if E::METADATA.requires_authentication {
if let Some(ref session) = *self.session.borrow() {
if let Some(ref session) = *session.borrow() {
url.query_pairs_mut().append_pair("access_token", session.access_token());
} else {
return Err(Error::AuthenticationRequired);
@ -202,15 +207,14 @@ where
}
}
Uri::from_str(url.as_ref())
Uri::from_str(url.as_ref().as_ref())
.map(move |uri| (uri, hyper_request))
.map_err(Error::from)
})
.and_then(move |(uri, mut hyper_request)| {
hyper_request.set_uri(uri);
self.hyper
.clone()
hyper
.request(hyper_request)
.map_err(Error::from)
})
@ -219,3 +223,13 @@ where
})
}
}
impl<C: Connect> Clone for Client<C> {
fn clone(&self) -> Client<C> {
Client {
homeserver_url: self.homeserver_url.clone(),
hyper: self.hyper.clone(),
session: self.session.clone(),
}
}
}