Merge pull request #7 from jplatte/master
Erase lifetimes from client member functions, add examples
This commit is contained in:
		
						commit
						6fec2ba9ec
					
				| @ -16,7 +16,6 @@ hyper = "0.11.1" | ||||
| ruma-api = "0.4.0" | ||||
| ruma-client-api = "0.1.0" | ||||
| ruma-identifiers = "0.11.0" | ||||
| serde = "1.0.9" | ||||
| serde_json = "1.0.2" | ||||
| serde_urlencoded = "0.5.1" | ||||
| tokio-core = "0.1.8" | ||||
| @ -30,6 +29,10 @@ version = "0.1.2" | ||||
| optional = true | ||||
| version = "0.1.4" | ||||
| 
 | ||||
| [dev-dependencies] | ||||
| futures-await = { git = 'https://github.com/alexcrichton/futures-await' } | ||||
| ruma-events = "0.9.0" | ||||
| 
 | ||||
| [features] | ||||
| default = ["tls"] | ||||
| tls = ["hyper-tls", "native-tls"] | ||||
|  | ||||
							
								
								
									
										88
									
								
								examples/hello_world.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								examples/hello_world.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,88 @@ | ||||
| #![feature(conservative_impl_trait)] | ||||
| #![feature(try_from)] | ||||
| 
 | ||||
| extern crate futures; | ||||
| extern crate ruma_client; | ||||
| extern crate ruma_events; | ||||
| extern crate ruma_identifiers; | ||||
| extern crate tokio_core; | ||||
| extern crate url; | ||||
| 
 | ||||
| use std::convert::TryFrom; | ||||
| use std::env; | ||||
| use std::process::exit; | ||||
| 
 | ||||
| use futures::Future; | ||||
| 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, Handle}; | ||||
| use url::Url; | ||||
| 
 | ||||
| // 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: &Handle, | ||||
|     homeserver_url: Url, | ||||
|     room: String, | ||||
| ) -> 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, r0::alias::get_alias::Request { | ||||
|             room_alias: RoomAliasId::try_from(&room[..]).unwrap(), | ||||
|         }) | ||||
|     })).and_then(clone!(client => move |response| { | ||||
|         let room_id = response.room_id; | ||||
| 
 | ||||
|         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 { | ||||
|                 room_id: room_id, | ||||
|                 event_type: EventType::RoomMessage, | ||||
|                 txn_id: "1".to_owned(), | ||||
|                 data: MessageEventContent::Text(TextMessageEventContent { | ||||
|                     body: "Hello World!".to_owned(), | ||||
|                     msgtype: MessageType::Text, | ||||
|                 }), | ||||
|             }) | ||||
|         }) | ||||
|     })).map(|_| ()) | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
|     let (homeserver_url, room) = match (env::args().nth(1), env::args().nth(2)) { | ||||
|         (Some(a), Some(b)) => (a, b), | ||||
|         _ => { | ||||
|             eprintln!("Usage: {} <homeserver_url> <room>", env::args().next().unwrap()); | ||||
|             exit(1) | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let mut core = Core::new().unwrap(); | ||||
|     let handle = core.handle(); | ||||
|     let server = Url::parse(&homeserver_url).unwrap(); | ||||
| 
 | ||||
|     core.run(hello_world(&handle, server, room)).unwrap(); | ||||
| } | ||||
							
								
								
									
										85
									
								
								examples/hello_world_await.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								examples/hello_world_await.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,85 @@ | ||||
| #![feature(conservative_impl_trait)] | ||||
| #![feature(generators)] | ||||
| #![feature(proc_macro)] | ||||
| #![feature(try_from)] | ||||
| 
 | ||||
| extern crate futures_await as futures; | ||||
| extern crate ruma_client; | ||||
| extern crate ruma_events; | ||||
| extern crate ruma_identifiers; | ||||
| extern crate tokio_core; | ||||
| extern crate url; | ||||
| 
 | ||||
| use std::convert::TryFrom; | ||||
| use std::env; | ||||
| use std::process::exit; | ||||
| 
 | ||||
| use futures::prelude::*; | ||||
| 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, Handle}; | ||||
| use url::Url; | ||||
| 
 | ||||
| fn hello_world( | ||||
|     tokio_handle: &Handle, | ||||
|     homeserver_url: Url, | ||||
|     room: String, | ||||
| ) -> impl Future<Item = (), Error = ruma_client::Error> + 'static { | ||||
|     let client = Client::https(tokio_handle, homeserver_url, None).unwrap(); | ||||
| 
 | ||||
|     async_block! { | ||||
|         await!(client.register_guest())?; | ||||
| 
 | ||||
|         let response = await!(r0::alias::get_alias::call( | ||||
|             client.clone(), | ||||
|             r0::alias::get_alias::Request { | ||||
|                 room_alias: RoomAliasId::try_from(&room[..]).unwrap(), | ||||
|             } | ||||
|         ))?; | ||||
| 
 | ||||
|         let room_id = response.room_id; | ||||
| 
 | ||||
|         await!(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, | ||||
|             } | ||||
|         ))?; | ||||
| 
 | ||||
|         await!(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(), | ||||
|                 data: MessageEventContent::Text(TextMessageEventContent { | ||||
|                     body: "Hello World!".to_owned(), | ||||
|                     msgtype: MessageType::Text, | ||||
|                 }), | ||||
|             } | ||||
|         ))?; | ||||
| 
 | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn main() { | ||||
|     let (homeserver_url, room) = match (env::args().nth(1), env::args().nth(2)) { | ||||
|         (Some(a), Some(b)) => (a, b), | ||||
|         _ => { | ||||
|             eprintln!("Usage: {} <homeserver_url> <room>", env::args().next().unwrap()); | ||||
|             exit(1) | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
| 
 | ||||
|     let mut core = Core::new().unwrap(); | ||||
|     let handle = core.handle(); | ||||
|     let server = Url::parse(&homeserver_url).unwrap(); | ||||
| 
 | ||||
|     core.run(hello_world(&handle, server, room)).unwrap(); | ||||
| } | ||||
| @ -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, | ||||
|             { | ||||
|  | ||||
							
								
								
									
										184
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										184
									
								
								src/lib.rs
									
									
									
									
									
								
							| @ -13,7 +13,6 @@ extern crate native_tls; | ||||
| extern crate ruma_api; | ||||
| extern crate ruma_client_api; | ||||
| extern crate ruma_identifiers; | ||||
| extern crate serde; | ||||
| extern crate serde_json; | ||||
| extern crate serde_urlencoded; | ||||
| extern crate tokio_core; | ||||
| @ -25,6 +24,7 @@ use std::rc::Rc; | ||||
| use std::str::FromStr; | ||||
| 
 | ||||
| use futures::future::{Future, FutureFrom, IntoFuture}; | ||||
| use futures::stream::{self, Stream}; | ||||
| use hyper::{Client as HyperClient, Uri}; | ||||
| use hyper::client::{Connect, HttpConnector}; | ||||
| #[cfg(feature = "hyper-tls")] | ||||
| @ -35,7 +35,6 @@ use ruma_api::Endpoint; | ||||
| use tokio_core::reactor::Handle; | ||||
| use url::Url; | ||||
| 
 | ||||
| use api::r0::session::login; | ||||
| pub use error::Error; | ||||
| pub use session::Session; | ||||
| 
 | ||||
| @ -45,24 +44,28 @@ mod error; | ||||
| mod session; | ||||
| 
 | ||||
| /// A client for the Matrix client-server API.
 | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct Client<C> | ||||
| #[derive(Debug)] | ||||
| pub struct Client<C: Connect>(Rc<ClientData<C>>); | ||||
| 
 | ||||
| /// Data contained in Client's Rc
 | ||||
| #[derive(Debug)] | ||||
| pub struct ClientData<C> | ||||
| where | ||||
|     C: Connect, | ||||
| { | ||||
|     homeserver_url: Url, | ||||
|     hyper: Rc<HyperClient<C>>, | ||||
|     hyper: HyperClient<C>, | ||||
|     session: 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, | ||||
|             hyper: Rc::new(HyperClient::configure().keep_alive(true).build(handle)), | ||||
|         Client(Rc::new(ClientData { | ||||
|             homeserver_url: homeserver_url, | ||||
|             hyper: HyperClient::configure().keep_alive(true).build(handle), | ||||
|             session: RefCell::new(session), | ||||
|         } | ||||
|         })) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -72,16 +75,16 @@ impl Client<HttpsConnector<HttpConnector>> { | ||||
|     pub fn https(handle: &Handle, homeserver_url: Url, session: Option<Session>) -> Result<Self, NativeTlsError> { | ||||
|         let connector = HttpsConnector::new(4, handle)?; | ||||
| 
 | ||||
|         Ok(Client { | ||||
|             homeserver_url, | ||||
|             hyper: Rc::new( | ||||
|         Ok(Client(Rc::new(ClientData { | ||||
|             homeserver_url: homeserver_url, | ||||
|             hyper: { | ||||
|                 HyperClient::configure() | ||||
|                     .connector(connector) | ||||
|                     .keep_alive(true) | ||||
|                     .build(handle), | ||||
|             ), | ||||
|                     .build(handle) | ||||
|             }, | ||||
|             session: RefCell::new(session), | ||||
|         }) | ||||
|         }))) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -93,41 +96,145 @@ 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, | ||||
|             hyper: Rc::new(hyper_client), | ||||
|         Client(Rc::new(ClientData { | ||||
|             homeserver_url: homeserver_url, | ||||
|             hyper: hyper_client, | ||||
|             session: RefCell::new(session), | ||||
|         } | ||||
|         })) | ||||
|     } | ||||
| 
 | ||||
|     /// Log in to the homeserver with a username and password.
 | ||||
|     pub fn log_in<'a>(&'a self, user: String, password: String) | ||||
|     -> impl Future<Item = (), Error = Error> + 'a { | ||||
|         let request = login::Request { | ||||
|     /// Log in with a username and password.
 | ||||
|     ///
 | ||||
|     /// 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(&self, user: String, password: String) | ||||
|     -> impl Future<Item = Session, Error = Error> { | ||||
|         use api::r0::session::login; | ||||
| 
 | ||||
|         let data = self.0.clone(); | ||||
| 
 | ||||
|         login::call(self.clone(), login::Request { | ||||
|             address: None, | ||||
|             login_type: login::LoginType::Password, | ||||
|             medium: None, | ||||
|             password, | ||||
|             user, | ||||
|         }).map(move |response| { | ||||
|             let session = Session::new(response.access_token, response.user_id); | ||||
|             *data.session.borrow_mut() = Some(session.clone()); | ||||
| 
 | ||||
|             session | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     /// 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(&self) -> impl Future<Item = Session, Error = Error> { | ||||
|         use api::r0::account::register; | ||||
| 
 | ||||
|         let data = self.0.clone(); | ||||
| 
 | ||||
|         register::call(self.clone(), register::Request { | ||||
|             auth: None, | ||||
|             bind_email: None, | ||||
|             device_id: None, | ||||
|             initial_device_display_name: None, | ||||
|             kind: Some(register::RegistrationKind::Guest), | ||||
|             password: None, | ||||
|             username: None, | ||||
|         }).map(move |response| { | ||||
|             let session = Session::new(response.access_token, response.user_id); | ||||
|             *data.session.borrow_mut() = Some(session.clone()); | ||||
| 
 | ||||
|             session | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     /// Register as a new user on this server.
 | ||||
|     ///
 | ||||
|     /// 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.
 | ||||
|     ///
 | ||||
|     /// 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( | ||||
|         &self, | ||||
|         username: Option<String>, | ||||
|         password: String, | ||||
|     ) -> impl Future<Item = Session, Error = Error> { | ||||
|         use api::r0::account::register; | ||||
| 
 | ||||
|         let data = self.0.clone(); | ||||
| 
 | ||||
|         register::call(self.clone(), register::Request { | ||||
|             auth: None, | ||||
|             bind_email: None, | ||||
|             device_id: None, | ||||
|             initial_device_display_name: None, | ||||
|             kind: Some(register::RegistrationKind::User), | ||||
|             password: Some(password), | ||||
|             username: username, | ||||
|         }).map(move |response| { | ||||
|             let session = Session::new(response.access_token, response.user_id); | ||||
|             *data.session.borrow_mut() = Some(session.clone()); | ||||
| 
 | ||||
|             session | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     /// Convenience method that represents repeated calls to the sync_events endpoint as a stream.
 | ||||
|     ///
 | ||||
|     /// If the since parameter is None, the first Item might take a significant time to arrive and
 | ||||
|     /// be deserialized, because it contains all events that have occured in the whole lifetime of
 | ||||
|     /// the logged-in users account and are visible to them.
 | ||||
|     pub fn sync( | ||||
|         &self, | ||||
|         filter: Option<api::r0::sync::sync_events::Filter>, | ||||
|         since: Option<String>, | ||||
|         set_presence: bool, | ||||
|     ) -> impl Stream<Item = api::r0::sync::sync_events::Response, Error = Error> { | ||||
|         use api::r0::sync::sync_events; | ||||
| 
 | ||||
|         let client = self.clone(); | ||||
|         let set_presence = if set_presence { | ||||
|             None | ||||
|         } else { | ||||
|             Some(sync_events::SetPresence::Offline) | ||||
|         }; | ||||
| 
 | ||||
|         login::call(self, request).and_then(move |response| { | ||||
|             *self.session.borrow_mut() = Some(Session::new(response.access_token, response.user_id)); | ||||
| 
 | ||||
|             Ok(()) | ||||
|         stream::unfold(since, move |since| { | ||||
|             Some( | ||||
|                 sync_events::call( | ||||
|                     client.clone(), | ||||
|                     sync_events::Request { | ||||
|                         filter: filter.clone(), | ||||
|                         since, | ||||
|                         full_state: None, | ||||
|                         set_presence: set_presence.clone(), | ||||
|                         timeout: None, | ||||
|                     }, | ||||
|                 ).map(|res| { | ||||
|                     let next_batch_clone = res.next_batch.clone(); | ||||
|                     (res, Some(next_batch_clone)) | ||||
|                 }) | ||||
|             ) | ||||
|         }) | ||||
|     } | ||||
| 
 | ||||
|     /// 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 data1 = self.0.clone(); | ||||
|         let data2 = self.0.clone(); | ||||
|         let mut url = self.0.homeserver_url.clone(); | ||||
| 
 | ||||
|         request | ||||
|             .try_into() | ||||
| @ -141,7 +248,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) = *data1.session.borrow() { | ||||
|                             url.query_pairs_mut().append_pair("access_token", session.access_token()); | ||||
|                         } else { | ||||
|                             return Err(Error::AuthenticationRequired); | ||||
| @ -149,15 +256,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() | ||||
|                 data2.hyper | ||||
|                     .request(hyper_request) | ||||
|                     .map_err(Error::from) | ||||
|             }) | ||||
| @ -166,3 +272,9 @@ where | ||||
|             }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl<C: Connect> Clone for Client<C> { | ||||
|     fn clone(&self) -> Client<C> { | ||||
|         Client(self.0.clone()) | ||||
|     } | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user