diff --git a/ruma-client/Cargo.toml b/ruma-client/Cargo.toml index cc70991f..969aa4fb 100644 --- a/ruma-client/Cargo.toml +++ b/ruma-client/Cargo.toml @@ -14,32 +14,11 @@ readme = "README.md" repository = "https://github.com/ruma/ruma" version = "0.5.0-alpha.2" -[dependencies] -assign = "1.1.1" -async-stream = "0.3.0" -futures-core = "0.3.8" -http = "0.2.2" -hyper = { version = "0.14.2", features = ["client", "tcp"] } -hyper-tls = { version = "0.5.0", optional = true } -hyper-rustls = { version = "0.22.1", optional = true, default-features = false } -ruma-api = { version = "=0.17.0-alpha.4", path = "../ruma-api" } -ruma-client-api = { version = "=0.10.0-alpha.3", path = "../ruma-client-api", features = ["client"] } -ruma-common = { version = "0.5.0", path = "../ruma-common" } -ruma-events = { version = "=0.22.0-alpha.3", path = "../ruma-events" } -ruma-identifiers = { version = "0.19.0", path = "../ruma-identifiers" } -ruma-serde = { version = "0.3.1", path = "../ruma-serde" } -serde = { version = "1.0.118", features = ["derive"] } -serde_json = "1.0.61" - -[dev-dependencies] -anyhow = "1.0.37" -ruma = { version = "0.0.3", path = "../ruma", features = ["client-api-c"] } -tokio = { version = "1.0.1", features = ["macros", "rt"] } -tokio-stream = { version = "0.1.1", default-features = false } - [features] default = ["http1", "http2", "tls-native"] +client-api = ["ruma-client-api"] + http1 = ["hyper/http1"] http2 = ["hyper/http2"] tls-native = ["hyper-tls", "_tls"] @@ -57,3 +36,34 @@ tls-rustls-webpki-roots = [ # Internal, not meant to be used directly _tls = [] _tls-rustls = ["_tls"] + +[dependencies] +assign = "1.1.1" +async-stream = "0.3.0" +futures-core = "0.3.8" +http = "0.2.2" +hyper = { version = "0.14.2", features = ["client", "tcp"] } +hyper-tls = { version = "0.5.0", optional = true } +hyper-rustls = { version = "0.22.1", optional = true, default-features = false } +ruma-api = { version = "=0.17.0-alpha.4", path = "../ruma-api" } +ruma-client-api = { version = "=0.10.0-alpha.3", path = "../ruma-client-api", optional = true, features = ["client"] } +ruma-common = { version = "0.5.0", path = "../ruma-common" } +ruma-events = { version = "=0.22.0-alpha.3", path = "../ruma-events" } +ruma-identifiers = { version = "0.19.0", path = "../ruma-identifiers" } +ruma-serde = { version = "0.3.1", path = "../ruma-serde" } +serde = { version = "1.0.118", features = ["derive"] } +serde_json = "1.0.61" + +[dev-dependencies] +anyhow = "1.0.37" +ruma = { version = "0.0.3", path = "../ruma", features = ["client-api-c"] } +tokio = { version = "1.0.1", features = ["macros", "rt"] } +tokio-stream = { version = "0.1.1", default-features = false } + +[[example]] +name = "hello_world" +required-features = ["client-api"] + +[[example]] +name = "message_log" +required-features = ["client-api"] diff --git a/ruma-client/src/client_api.rs b/ruma-client/src/client_api.rs new file mode 100644 index 00000000..533d8f5d --- /dev/null +++ b/ruma-client/src/client_api.rs @@ -0,0 +1,140 @@ +use std::time::Duration; + +use assign::assign; +use async_stream::try_stream; +use futures_core::stream::Stream; +use ruma_client_api::r0::sync::sync_events::{ + Filter as SyncFilter, Request as SyncRequest, Response as SyncResponse, +}; +use ruma_common::presence::PresenceState; +use ruma_identifiers::DeviceId; + +use super::{Client, Error, Identification, Session}; + +impl Client { + /// 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 async fn log_in( + &self, + user: &str, + password: &str, + device_id: Option<&DeviceId>, + initial_device_display_name: Option<&str>, + ) -> Result> { + use ruma_client_api::r0::session::login::{ + LoginInfo, Request as LoginRequest, UserIdentifier, + }; + + let response = self + .request(assign!( + LoginRequest::new( + LoginInfo::Password { identifier: UserIdentifier::MatrixId(user), password } + ), { + device_id, + initial_device_display_name, + } + )) + .await?; + + let session = Session { + access_token: response.access_token, + identification: Some(Identification { + device_id: response.device_id, + user_id: response.user_id, + }), + }; + *self.0.session.lock().unwrap() = Some(session.clone()); + + Ok(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 async fn register_guest( + &self, + ) -> Result> { + use ruma_client_api::r0::account::register::{self, RegistrationKind}; + + let response = self + .request(assign!(register::Request::new(), { kind: RegistrationKind::Guest })) + .await?; + + let session = Session { + // since we supply inhibit_login: false above, the access token needs to be there + // TODO: maybe unwrap is not the best solution though + access_token: response.access_token.unwrap(), + identification: Some(Identification { + // same as access_token + device_id: response.device_id.unwrap(), + user_id: response.user_id, + }), + }; + *self.0.session.lock().unwrap() = Some(session.clone()); + + Ok(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 async fn register_user( + &self, + username: Option<&str>, + password: &str, + ) -> Result> { + use ruma_client_api::r0::account::register; + + let response = self + .request(assign!(register::Request::new(), { username, password: Some(password) })) + .await?; + + let session = Session { + // since we supply inhibit_login: false above, the access token needs to be there + // TODO: maybe unwrap is not the best solution though + access_token: response.access_token.unwrap(), + identification: Some(Identification { + // same as access_token + device_id: response.device_id.unwrap(), + user_id: response.user_id, + }), + }; + *self.0.session.lock().unwrap() = Some(session.clone()); + + Ok(session) + } + + /// Convenience method that represents repeated calls to the sync_events endpoint as a stream. + pub fn sync<'a>( + &self, + filter: Option<&'a SyncFilter<'a>>, + mut since: String, + set_presence: &'a PresenceState, + timeout: Option, + ) -> impl Stream>> + 'a { + let client = self.clone(); + try_stream! { + loop { + let response = client + .request(assign!(SyncRequest::new(), { + filter, + since: Some(&since), + set_presence, + timeout, + })) + .await?; + + since = response.next_batch.clone(); + yield response; + } + } + } +} diff --git a/ruma-client/src/lib.rs b/ruma-client/src/lib.rs index 4ca38554..d62e35e1 100644 --- a/ruma-client/src/lib.rs +++ b/ruma-client/src/lib.rs @@ -7,7 +7,7 @@ //! Begin by creating a `Client` type, usually using the `https` method for a client that supports //! secure connections, and then logging in: //! -//! ```no_run +//! ```ignore //! use ruma_client::Client; //! //! let work = async { @@ -43,7 +43,7 @@ //! For the standard use case of synchronizing with the homeserver (i.e. getting all the latest //! events), use the `Client::sync`: //! -//! ```no_run +//! ```ignore //! use std::time::Duration; //! //! # use ruma_client::Client; @@ -105,22 +105,16 @@ use std::{ collections::BTreeMap, sync::{Arc, Mutex}, - time::Duration, }; use assign::assign; -use async_stream::try_stream; -use futures_core::stream::Stream; use http::{uri::Uri, Response as HttpResponse}; use hyper::client::{Client as HyperClient, HttpConnector}; use ruma_api::{AuthScheme, OutgoingRequest, SendAccessToken}; -use ruma_client_api::r0::sync::sync_events::{ - Filter as SyncFilter, Request as SyncRequest, Response as SyncResponse, -}; -use ruma_common::presence::PresenceState; -use ruma_identifiers::DeviceId; use ruma_serde::urlencoded; +#[cfg(feature = "client-api")] +mod client_api; mod error; mod session; @@ -203,132 +197,6 @@ impl Client { self.0.session.lock().expect("session mutex was poisoned").clone() } - /// 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 async fn log_in( - &self, - user: &str, - password: &str, - device_id: Option<&DeviceId>, - initial_device_display_name: Option<&str>, - ) -> Result> { - use ruma_client_api::r0::session::login::{ - LoginInfo, Request as LoginRequest, UserIdentifier, - }; - - let response = self - .request(assign!( - LoginRequest::new( - LoginInfo::Password { identifier: UserIdentifier::MatrixId(user), password } - ), { - device_id, - initial_device_display_name, - } - )) - .await?; - - let session = Session { - access_token: response.access_token, - identification: Some(Identification { - device_id: response.device_id, - user_id: response.user_id, - }), - }; - *self.0.session.lock().unwrap() = Some(session.clone()); - - Ok(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 async fn register_guest( - &self, - ) -> Result> { - use ruma_client_api::r0::account::register::{self, RegistrationKind}; - - let response = self - .request(assign!(register::Request::new(), { kind: RegistrationKind::Guest })) - .await?; - - let session = Session { - // since we supply inhibit_login: false above, the access token needs to be there - // TODO: maybe unwrap is not the best solution though - access_token: response.access_token.unwrap(), - identification: Some(Identification { - // same as access_token - device_id: response.device_id.unwrap(), - user_id: response.user_id, - }), - }; - *self.0.session.lock().unwrap() = Some(session.clone()); - - Ok(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 async fn register_user( - &self, - username: Option<&str>, - password: &str, - ) -> Result> { - use ruma_client_api::r0::account::register; - - let response = self - .request(assign!(register::Request::new(), { username, password: Some(password) })) - .await?; - - let session = Session { - // since we supply inhibit_login: false above, the access token needs to be there - // TODO: maybe unwrap is not the best solution though - access_token: response.access_token.unwrap(), - identification: Some(Identification { - // same as access_token - device_id: response.device_id.unwrap(), - user_id: response.user_id, - }), - }; - *self.0.session.lock().unwrap() = Some(session.clone()); - - Ok(session) - } - - /// Convenience method that represents repeated calls to the sync_events endpoint as a stream. - pub fn sync<'a>( - &self, - filter: Option<&'a SyncFilter<'a>>, - mut since: String, - set_presence: &'a PresenceState, - timeout: Option, - ) -> impl Stream>> + 'a { - let client = self.clone(); - try_stream! { - loop { - let response = client - .request(assign!(SyncRequest::new(), { - filter, - since: Some(&since), - set_presence, - timeout, - })) - .await?; - - since = response.next_batch.clone(); - yield response; - } - } - } - /// Makes a request to a Matrix API endpoint. pub async fn request( &self,