From 3dae62ec253f2a85b7f411cff14b4be70b48d16e Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 12 Jul 2017 03:25:00 +1000 Subject: [PATCH] Remove Client lifetime from Futures by using Rc and clones --- examples/hello_world.rs | 52 ++++++++++++++++++--------- src/api.rs | 6 ++-- src/lib.rs | 78 ++++++++++++++++++++++++----------------- 3 files changed, 85 insertions(+), 51 deletions(-) diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 16149c20..7f8d43eb 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -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) --> impl Future + '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 + '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) 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(); } diff --git a/src/api.rs b/src/api.rs index 4049e448..2983a307 100644 --- a/src/api.rs +++ b/src/api.rs @@ -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, + pub fn call( + client: Client, request: Request, - ) -> impl Future + 'a + ) -> impl Future where C: Connect, { diff --git a/src/lib.rs b/src/lib.rs index 587f5ff3..a33e0b27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -45,23 +45,23 @@ mod error; mod session; /// A client for the Matrix client-server API. -#[derive(Clone, Debug)] +#[derive(Debug)] pub struct Client where C: Connect, { - homeserver_url: Url, + homeserver_url: Rc, hyper: Rc>, - session: RefCell>, + session: Rc>>, } impl Client { /// Creates a new client for making HTTP requests to the given homeserver. pub fn new(handle: &Handle, homeserver_url: Url, session: Option) -> 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> { 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, homeserver_url: Url, session: Option) -> 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 + 'a { + pub fn log_in(&self, user: String, password: String) + -> impl Future { 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 + 'a { + pub fn register_guest(&self) -> impl Future { 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, password: String, - ) -> impl Future + 'a { + ) -> impl Future { 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( + self, request: ::Request, - ) -> impl Future + 'a + ) -> impl Future where E: 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 Clone for Client { + fn clone(&self) -> Client { + Client { + homeserver_url: self.homeserver_url.clone(), + hyper: self.hyper.clone(), + session: self.session.clone(), + } + } +}