diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b405bc8b..682d6c7a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,9 +120,6 @@ jobs: - name: Check All Features cmd: stable-all - - name: Check Client - cmd: stable-client - - name: Check Common cmd: stable-common diff --git a/Cargo.toml b/Cargo.toml index 685bb813..9bdb87b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,19 +18,18 @@ http = "1.1.0" js_int = "0.2.2" maplit = "1.0.2" rand = "0.8.5" -ruma-appservice-api = { version = "0.10.0", path = "crates/ruma-appservice-api" } -ruma-common = { version = "0.13.0", path = "crates/ruma-common" } -ruma-client = { version = "0.13.0", path = "crates/ruma-client" } -ruma-client-api = { version = "0.18.0", path = "crates/ruma-client-api" } -ruma-events = { version = "0.28.1", path = "crates/ruma-events" } -ruma-federation-api = { version = "0.9.0", path = "crates/ruma-federation-api" } -ruma-html = { version = "0.2.0", path = "crates/ruma-html" } -ruma-identifiers-validation = { version = "0.9.5", path = "crates/ruma-identifiers-validation" } -ruma-identity-service-api = { version = "0.9.0", path = "crates/ruma-identity-service-api" } -ruma-macros = { version = "=0.13.0", path = "crates/ruma-macros" } -ruma-push-gateway-api = { version = "0.9.0", path = "crates/ruma-push-gateway-api" } -ruma-signatures = { version = "0.15.0", path = "crates/ruma-signatures" } -ruma-server-util = { version = "0.3.0", path = "crates/ruma-server-util" } +ruma-appservice-api = { version = "0.12.1", path = "crates/ruma-appservice-api" } +ruma-client-api = { version = "0.20.1", path = "crates/ruma-client-api" } +ruma-common = { version = "0.15.1", path = "crates/ruma-common" } +ruma-events = { version = "0.30.1", path = "crates/ruma-events" } +ruma-federation-api = { version = "0.11.0", path = "crates/ruma-federation-api" } +ruma-html = { version = "0.4.0", path = "crates/ruma-html" } +ruma-identifiers-validation = { version = "0.10.1", path = "crates/ruma-identifiers-validation" } +ruma-identity-service-api = { version = "0.11.0", path = "crates/ruma-identity-service-api" } +ruma-macros = { version = "=0.15.1", path = "crates/ruma-macros" } +ruma-push-gateway-api = { version = "0.11.0", path = "crates/ruma-push-gateway-api" } +ruma-server-util = { version = "0.5.0", path = "crates/ruma-server-util" } +ruma-signatures = { version = "0.17.0", path = "crates/ruma-signatures" } #ruma-state-res = { version = "0.11.0", path = "crates/ruma-state-res" } serde = { version = "1.0.164", features = ["derive"] } serde_html_form = "0.2.0" diff --git a/README.md b/README.md index ae928eb2..3b81ca4b 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,8 @@ ruma = { git = "https://github.com/ruma/ruma", branch = "main", features = ["... them as a user. Check out the documentation [on docs.rs][docs] (or on [docs.ruma.dev][unstable-docs] if you use use the git dependency). +You can find a low level Matrix client in the [ruma-client repository](https://github.com/ruma/ruma-client). + You can also find a small number of examples in our dedicated [examples repository](https://github.com/ruma/examples). diff --git a/crates/ruma-client/.gitignore b/crates/ruma-client/.gitignore deleted file mode 100644 index 1b72444a..00000000 --- a/crates/ruma-client/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/Cargo.lock -/target diff --git a/crates/ruma-client/README.md b/crates/ruma-client/README.md deleted file mode 100644 index 9f37f902..00000000 --- a/crates/ruma-client/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# ruma-client - -[![crates.io page](https://img.shields.io/crates/v/ruma-client.svg)](https://crates.io/crates/ruma-client) -[![docs.rs page](https://docs.rs/ruma-client/badge.svg)](https://docs.rs/ruma-client/) -![license: MIT](https://img.shields.io/crates/l/ruma-client.svg) - -**ruma-client** is a low-level [Matrix][] client library for [Rust][]. - -[Matrix]: https://matrix.org/ -[Rust]: https://www.rust-lang.org/ - -## Status - -This project is a work in progress and not ready for production usage yet. If you are looking to -build a client application or a bot, you are likely going to have a better time using the Matrix -Rust SDK, which also builds on the other Ruma crates but provides a higher-level API. diff --git a/crates/ruma-client/build.rs b/crates/ruma-client/build.rs deleted file mode 100644 index ef92bd6a..00000000 --- a/crates/ruma-client/build.rs +++ /dev/null @@ -1,22 +0,0 @@ -use std::{env, process}; - -fn main() { - // Prevent unnecessary rerunning of this build script - println!("cargo:rerun-if-changed=build.rs"); - - let tls_features = [ - ("tls-native", env::var_os("CARGO_FEATURE_TLS_NATIVE").is_some()), - ("tls-rustls-native-roots", env::var_os("CARGO_FEATURE_TLS_RUSTLS_NATIVE_ROOTS").is_some()), - ("tls-rustls-webpki-roots", env::var_os("CARGO_FEATURE_TLS_RUSTLS_WEBPKI_ROOTS").is_some()), - ]; - - if tls_features.iter().filter(|(_, a)| *a).count() > 1 { - eprintln!("error: Only one tls features can be enabled."); - - for (f, a) in &tls_features { - eprintln!(" {f}: {}", if *a { "enabled" } else { "disabled" }); - } - - process::exit(1); - } -} diff --git a/crates/ruma-client/src/client.rs b/crates/ruma-client/src/client.rs deleted file mode 100644 index cf010c2c..00000000 --- a/crates/ruma-client/src/client.rs +++ /dev/null @@ -1,229 +0,0 @@ -use std::{ - sync::{Arc, Mutex}, - time::Duration, -}; - -use assign::assign; -use async_stream::try_stream; -use futures_core::stream::Stream; -use ruma_client_api::{ - account::register::{self, RegistrationKind}, - session::login::{self, v3::LoginInfo}, - sync::sync_events, - uiaa::UserIdentifier, -}; -use ruma_common::{ - api::{MatrixVersion, OutgoingRequest, SendAccessToken}, - presence::PresenceState, - DeviceId, UserId, -}; - -use crate::{ - add_user_id_to_query, send_customized_request, Error, HttpClient, ResponseError, ResponseResult, -}; - -mod builder; - -pub use self::builder::ClientBuilder; - -/// A client for the Matrix client-server API. -#[derive(Clone, Debug)] -pub struct Client(Arc>); - -/// Data contained in Client's Rc -#[derive(Debug)] -struct ClientData { - /// The URL of the homeserver to connect to. - homeserver_url: String, - - /// The underlying HTTP client. - http_client: C, - - /// The access token, if logged in. - access_token: Mutex>, - - /// The (known) Matrix versions the homeserver supports. - supported_matrix_versions: Vec, -} - -impl Client<()> { - /// Creates a new client builder. - pub fn builder() -> ClientBuilder { - ClientBuilder::new() - } -} - -impl Client { - /// Get a copy of the current `access_token`, if any. - /// - /// Useful for serializing and persisting the session to be restored later. - pub fn access_token(&self) -> Option { - self.0.access_token.lock().expect("session mutex was poisoned").clone() - } -} - -impl Client { - /// Makes a request to a Matrix API endpoint. - pub async fn send_request(&self, request: R) -> ResponseResult { - self.send_customized_request(request, |_| Ok(())).await - } - - /// Makes a request to a Matrix API endpoint including additional URL parameters. - pub async fn send_customized_request( - &self, - request: R, - customize: F, - ) -> ResponseResult - where - R: OutgoingRequest, - F: FnOnce(&mut http::Request) -> Result<(), ResponseError>, - { - let access_token = self.access_token(); - let send_access_token = match access_token.as_deref() { - Some(at) => SendAccessToken::IfRequired(at), - None => SendAccessToken::None, - }; - - send_customized_request( - &self.0.http_client, - &self.0.homeserver_url, - send_access_token, - &self.0.supported_matrix_versions, - request, - customize, - ) - .await - } - - /// Makes a request to a Matrix API endpoint as a virtual user. - /// - /// This method is meant to be used by application services when interacting with the - /// client-server API. - pub async fn send_request_as( - &self, - user_id: &UserId, - request: R, - ) -> ResponseResult { - self.send_customized_request(request, add_user_id_to_query::(user_id)).await - } - - /// Log in with a username and password. - /// - /// In contrast to [`send_request`][Self::send_request], this method stores the access token - /// returned by the endpoint in this client, in addition to returning it. - pub async fn log_in( - &self, - user: &str, - password: &str, - device_id: Option<&DeviceId>, - initial_device_display_name: Option<&str>, - ) -> Result> { - let login_info = LoginInfo::Password(login::v3::Password::new( - UserIdentifier::UserIdOrLocalpart(user.to_owned()), - password.to_owned(), - )); - let response = self - .send_request(assign!(login::v3::Request::new(login_info), { - device_id: device_id.map(ToOwned::to_owned), - initial_device_display_name: initial_device_display_name.map(ToOwned::to_owned), - })) - .await?; - - *self.0.access_token.lock().unwrap() = Some(response.access_token.clone()); - - Ok(response) - } - - /// Register as a guest. - /// - /// In contrast to [`send_request`][Self::send_request], this method stores the access token - /// returned by the endpoint in this client, in addition to returning it. - pub async fn register_guest( - &self, - ) -> Result> { - let response = self - .send_request(assign!(register::v3::Request::new(), { kind: RegistrationKind::Guest })) - .await?; - - self.0.access_token.lock().unwrap().clone_from(&response.access_token); - - Ok(response) - } - - /// Register as a new user on this server. - /// - /// In contrast to [`send_request`][Self::send_request], this method stores the access token - /// returned by the endpoint in this client, in addition to 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> { - let response = self - .send_request(assign!(register::v3::Request::new(), { - username: username.map(ToOwned::to_owned), - password: Some(password.to_owned()) - })) - .await?; - - self.0.access_token.lock().unwrap().clone_from(&response.access_token); - - Ok(response) - } - - /// Convenience method that represents repeated calls to the sync_events endpoint as a stream. - /// - /// # Example: - /// - /// ```no_run - /// use std::time::Duration; - /// - /// # use ruma_common::presence::PresenceState; - /// # use tokio_stream::{StreamExt as _}; - /// # let homeserver_url = "https://example.com".to_owned(); - /// # async { - /// # let client = ruma_client::Client::builder() - /// # .homeserver_url(homeserver_url) - /// # .build::() - /// # .await?; - /// # let next_batch_token = String::new(); - /// let mut sync_stream = Box::pin(client.sync( - /// None, - /// next_batch_token, - /// PresenceState::Online, - /// Some(Duration::from_secs(30)), - /// )); - /// while let Some(response) = sync_stream.try_next().await? { - /// // Do something with the data in the response... - /// } - /// # Result::<(), ruma_client::Error<_, _>>::Ok(()) - /// # }; - /// ``` - pub fn sync( - &self, - filter: Option, - mut since: String, - set_presence: PresenceState, - timeout: Option, - ) -> impl Stream>> - + '_ { - try_stream! { - loop { - let response = self - .send_request(assign!(sync_events::v3::Request::new(), { - filter: filter.clone(), - since: Some(since.clone()), - set_presence: set_presence.clone(), - timeout, - })) - .await?; - - since.clone_from(&response.next_batch); - yield response; - } - } - } -} diff --git a/crates/ruma-client/src/client/builder.rs b/crates/ruma-client/src/client/builder.rs deleted file mode 100644 index b87643b3..00000000 --- a/crates/ruma-client/src/client/builder.rs +++ /dev/null @@ -1,96 +0,0 @@ -use std::sync::{Arc, Mutex}; - -use ruma_client_api::discovery::get_supported_versions; -use ruma_common::api::{MatrixVersion, SendAccessToken}; - -use super::{Client, ClientData}; -use crate::{DefaultConstructibleHttpClient, Error, HttpClient, HttpClientExt}; - -/// A [`Client`] builder. -/// -/// This type can be used to construct a `Client` through a few method calls. -pub struct ClientBuilder { - homeserver_url: Option, - access_token: Option, - supported_matrix_versions: Option>, -} - -impl ClientBuilder { - pub(super) fn new() -> Self { - Self { homeserver_url: None, access_token: None, supported_matrix_versions: None } - } - - /// Set the homeserver URL. - /// - /// The homeserver URL must be set before calling [`build()`][Self::build] or - /// [`http_client()`][Self::http_client]. - pub fn homeserver_url(self, url: String) -> Self { - Self { homeserver_url: Some(url), ..self } - } - - /// Set the access token. - pub fn access_token(self, access_token: Option) -> Self { - Self { access_token, ..self } - } - - /// Set the supported Matrix versions. - /// - /// This method generally *shouldn't* be called. The [`build()`][Self::build] or - /// [`http_client()`][Self::http_client] method will take care of doing a - /// [`get_supported_versions`] request to find out about the supported versions. - pub fn supported_matrix_versions(self, versions: Vec) -> Self { - Self { supported_matrix_versions: Some(versions), ..self } - } - - /// Finish building the [`Client`]. - /// - /// Uses [`DefaultConstructibleHttpClient::default()`] to create an HTTP client instance. - /// Unless the supported Matrix versions were manually set via - /// [`supported_matrix_versions`][Self::supported_matrix_versions], this will do a - /// [`get_supported_versions`] request to find out about the supported versions. - pub async fn build(self) -> Result, Error> - where - C: DefaultConstructibleHttpClient, - { - self.http_client(C::default()).await - } - - /// Set the HTTP client to finish building the [`Client`]. - /// - /// Unless the supported Matrix versions were manually set via - /// [`supported_matrix_versions`][Self::supported_matrix_versions], this will do a - /// [`get_supported_versions`] request to find out about the supported versions. - pub async fn http_client( - self, - http_client: C, - ) -> Result, Error> - where - C: HttpClient, - { - let homeserver_url = self - .homeserver_url - .expect("homeserver URL has to be set prior to calling .build() or .http_client()"); - - let supported_matrix_versions = match self.supported_matrix_versions { - Some(versions) => versions, - None => http_client - .send_matrix_request( - &homeserver_url, - SendAccessToken::None, - &[MatrixVersion::V1_0], - get_supported_versions::Request::new(), - ) - .await? - .known_versions() - .into_iter() - .collect(), - }; - - Ok(Client(Arc::new(ClientData { - homeserver_url, - http_client, - access_token: Mutex::new(self.access_token), - supported_matrix_versions, - }))) - } -} diff --git a/crates/ruma-client/src/error.rs b/crates/ruma-client/src/error.rs deleted file mode 100644 index 574f0fa3..00000000 --- a/crates/ruma-client/src/error.rs +++ /dev/null @@ -1,79 +0,0 @@ -//! Error conditions. - -use std::fmt::{self, Debug, Display, Formatter}; - -use ruma_common::api::error::{FromHttpResponseError, IntoHttpError}; - -/// An error that can occur during client operations. -#[derive(Debug)] -#[non_exhaustive] -pub enum Error { - /// Queried endpoint requires authentication but was called on an anonymous client. - AuthenticationRequired, - - /// Construction of the HTTP request failed (this should never happen). - IntoHttp(IntoHttpError), - - /// The request's URL is invalid (this should never happen). - Url(http::Error), - - /// Couldn't obtain an HTTP response (e.g. due to network or DNS issues). - Response(E), - - /// Converting the HTTP response to one of ruma's types failed. - FromHttpResponse(FromHttpResponseError), -} - -#[cfg(feature = "client-api")] -impl Error { - /// If `self` is a server error in the `errcode` + `error` format expected - /// for client-server API endpoints, returns the error kind (`errcode`). - pub fn error_kind(&self) -> Option<&ruma_client_api::error::ErrorKind> { - use as_variant::as_variant; - use ruma_client_api::error::FromHttpResponseErrorExt as _; - - as_variant!(self, Self::FromHttpResponse)?.error_kind() - } -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - match self { - Self::AuthenticationRequired => { - write!(f, "The queried endpoint requires authentication but was called with an anonymous client.") - } - Self::IntoHttp(err) => write!(f, "HTTP request construction failed: {err}"), - Self::Url(err) => write!(f, "Invalid URL: {err}"), - Self::Response(err) => write!(f, "Couldn't obtain a response: {err}"), - Self::FromHttpResponse(err) => write!(f, "HTTP response conversion failed: {err}"), - } - } -} - -impl From for Error { - fn from(err: IntoHttpError) -> Self { - Error::IntoHttp(err) - } -} - -#[doc(hidden)] -impl From for Error { - fn from(err: http::uri::InvalidUri) -> Self { - Error::Url(err.into()) - } -} - -#[doc(hidden)] -impl From for Error { - fn from(err: http::uri::InvalidUriParts) -> Self { - Error::Url(err.into()) - } -} - -impl From> for Error { - fn from(err: FromHttpResponseError) -> Self { - Error::FromHttpResponse(err) - } -} - -impl std::error::Error for Error {} diff --git a/crates/ruma-client/src/http_client.rs b/crates/ruma-client/src/http_client.rs deleted file mode 100644 index ca121634..00000000 --- a/crates/ruma-client/src/http_client.rs +++ /dev/null @@ -1,148 +0,0 @@ -//! This module contains an abstraction for HTTP clients as well as friendly-named re-exports of -//! client types that implement this trait. - -use std::{future::Future, pin::Pin}; - -use bytes::BufMut; -use ruma_common::{ - api::{MatrixVersion, OutgoingRequest, SendAccessToken}, - UserId, -}; - -use crate::{add_user_id_to_query, ResponseError, ResponseResult}; - -#[cfg(feature = "hyper")] -mod hyper; -#[cfg(feature = "reqwest")] -mod reqwest; - -#[cfg(feature = "hyper")] -pub use self::hyper::Hyper; -#[cfg(feature = "hyper-native-tls")] -pub use self::hyper::HyperNativeTls; -#[cfg(feature = "hyper-rustls")] -pub use self::hyper::HyperRustls; -#[cfg(feature = "reqwest")] -pub use self::reqwest::Reqwest; - -/// An HTTP client that can be used to send requests to a Matrix homeserver. -pub trait HttpClient: Sync { - /// The type to use for `try_into_http_request`. - type RequestBody: Default + BufMut + Send; - - /// The type to use for `try_from_http_response`. - type ResponseBody: AsRef<[u8]>; - - /// The error type for the `send_request` function. - type Error: Send + Unpin; - - /// Send an `http::Request` to get back an `http::Response`. - fn send_http_request( - &self, - req: http::Request, - ) -> impl Future, Self::Error>> + Send; -} - -/// An HTTP client that has a default configuration. -pub trait DefaultConstructibleHttpClient: HttpClient { - /// Creates a new HTTP client with default configuration. - fn default() -> Self; -} - -/// Convenience functionality on top of `HttpClient`. -/// -/// If you want to build your own matrix client type instead of using `ruma_client::Client`, this -/// trait should make that relatively easy. -pub trait HttpClientExt: HttpClient { - /// Send a strongly-typed matrix request to get back a strongly-typed response. - // TODO: `R: 'a` bound should not be needed - fn send_matrix_request<'a, R: OutgoingRequest + 'a>( - &'a self, - homeserver_url: &str, - access_token: SendAccessToken<'_>, - for_versions: &[MatrixVersion], - request: R, - ) -> Pin> + 'a + Send>> { - self.send_customized_matrix_request( - homeserver_url, - access_token, - for_versions, - request, - |_| Ok(()), - ) - } - - /// Turn a strongly-typed matrix request into an `http::Request`, customize it and send it to - /// get back a strongly-typed response. - // TODO: `R: 'a` and `F: 'a` should not be needed - fn send_customized_matrix_request<'a, R, F>( - &'a self, - homeserver_url: &str, - access_token: SendAccessToken<'_>, - for_versions: &[MatrixVersion], - request: R, - customize: F, - ) -> Pin> + 'a + Send>> - where - R: OutgoingRequest + 'a, - F: FnOnce(&mut http::Request) -> Result<(), ResponseError> + 'a, - { - Box::pin(crate::send_customized_request( - self, - homeserver_url, - access_token, - for_versions, - request, - customize, - )) - } - - /// Turn a strongly-typed matrix request into an `http::Request`, add a `user_id` query - /// parameter to it and send it to get back a strongly-typed response. - /// - /// This method is meant to be used by application services when interacting with the - /// client-server API. - fn send_matrix_request_as<'a, R: OutgoingRequest + 'a>( - &'a self, - homeserver_url: &str, - access_token: SendAccessToken<'_>, - for_versions: &[MatrixVersion], - user_id: &'a UserId, - request: R, - ) -> Pin> + 'a>> { - self.send_customized_matrix_request( - homeserver_url, - access_token, - for_versions, - request, - add_user_id_to_query::(user_id), - ) - } -} - -impl HttpClientExt for T {} - -#[doc(hidden)] -#[derive(Debug)] -#[allow(clippy::exhaustive_structs)] -pub struct Dummy; - -impl HttpClient for Dummy { - type RequestBody = Vec; - type ResponseBody = Vec; - type Error = (); - - #[allow(clippy::diverging_sub_expression)] - async fn send_http_request( - &self, - _req: http::Request, - ) -> Result, Self::Error> { - unimplemented!("this client only exists to allow doctests to compile") - } -} - -impl DefaultConstructibleHttpClient for Dummy { - fn default() -> Self { - Dummy - } -} diff --git a/crates/ruma-client/src/http_client/hyper.rs b/crates/ruma-client/src/http_client/hyper.rs deleted file mode 100644 index 5df1faf6..00000000 --- a/crates/ruma-client/src/http_client/hyper.rs +++ /dev/null @@ -1,62 +0,0 @@ -use bytes::{Bytes, BytesMut}; -use http_body_util::{BodyExt as _, Full}; -use hyper_util::{ - client::legacy::connect::{Connect, HttpConnector}, - rt::TokioExecutor, -}; - -use super::{DefaultConstructibleHttpClient, HttpClient}; - -/// A hyper HTTP client. -/// -/// The default connector is rarely useful, since it doesn't support `https`. -pub type Hyper = hyper_util::client::legacy::Client>; - -/// A hyper HTTP client using native-tls for TLS support. -#[cfg(feature = "hyper-native-tls")] -pub type HyperNativeTls = Hyper>; - -/// A hyper HTTP client using rustls for TLS support. -/// -/// This client does not implement `DefaultConstructibleHttpClient`. -/// To use it, you need to manually create an instance. -#[cfg(feature = "hyper-rustls")] -pub type HyperRustls = Hyper>; - -impl HttpClient for Hyper -where - C: Connect + Clone + Send + Sync + 'static, -{ - type RequestBody = BytesMut; - type ResponseBody = Bytes; - type Error = Box; - - async fn send_http_request( - &self, - req: http::Request, - ) -> Result, Self::Error> { - let (head, body) = - self.request(req.map(|body| Full::new(body.freeze()))).await?.into_parts(); - - // FIXME: Use aggregate instead of to_bytes once serde_json can parse from a reader at a - // comparable speed as reading from a slice: https://github.com/serde-rs/json/issues/160 - let body = body.collect().await?.to_bytes(); - Ok(http::Response::from_parts(head, body)) - } -} - -#[cfg(feature = "hyper")] -impl DefaultConstructibleHttpClient for Hyper { - fn default() -> Self { - hyper_util::client::legacy::Client::builder(TokioExecutor::new()) - .build(HttpConnector::new()) - } -} - -#[cfg(feature = "hyper-native-tls")] -impl DefaultConstructibleHttpClient for HyperNativeTls { - fn default() -> Self { - hyper_util::client::legacy::Client::builder(TokioExecutor::new()) - .build(hyper_tls::HttpsConnector::new()) - } -} diff --git a/crates/ruma-client/src/http_client/reqwest.rs b/crates/ruma-client/src/http_client/reqwest.rs deleted file mode 100644 index 2877daa9..00000000 --- a/crates/ruma-client/src/http_client/reqwest.rs +++ /dev/null @@ -1,37 +0,0 @@ -use std::mem; - -use bytes::{Bytes, BytesMut}; - -use super::{DefaultConstructibleHttpClient, HttpClient}; - -/// The `reqwest` crate's `Client`. -pub type Reqwest = reqwest::Client; - -impl HttpClient for Reqwest { - type RequestBody = BytesMut; - type ResponseBody = Bytes; - type Error = reqwest::Error; - - async fn send_http_request( - &self, - req: http::Request, - ) -> Result, reqwest::Error> { - let req = req.map(|body| body.freeze()).try_into()?; - let mut res = self.execute(req).await?; - - let mut http_builder = - http::Response::builder().status(res.status()).version(res.version()); - mem::swap( - http_builder.headers_mut().expect("http::response::Builder to be usable"), - res.headers_mut(), - ); - - Ok(http_builder.body(res.bytes().await?).expect("http::Response construction to work")) - } -} - -impl DefaultConstructibleHttpClient for Reqwest { - fn default() -> Self { - reqwest::Client::new() - } -} diff --git a/crates/ruma-client/src/lib.rs b/crates/ruma-client/src/lib.rs deleted file mode 100644 index 1910ca07..00000000 --- a/crates/ruma-client/src/lib.rs +++ /dev/null @@ -1,197 +0,0 @@ -#![doc(html_favicon_url = "https://ruma.dev/favicon.ico")] -#![doc(html_logo_url = "https://ruma.dev/images/logo.png")] -//! A minimal [Matrix](https://matrix.org/) client library. -//! -//! # Usage -//! -//! Begin by creating a `Client`, selecting one of the type aliases from `ruma_client::http_client` -//! for the generic parameter. For the client API, there are login and registration methods -//! provided for the client (feature `client-api`): -//! -//! ```ignore -//! # // HACK: "ignore" the doctest here because client.log_in needs client-api feature. -//! // type HttpClient = ruma_client::http_client::_; -//! # type HttpClient = ruma_client::http_client::Dummy; -//! # let work = async { -//! let homeserver_url = "https://example.com".to_owned(); -//! let client = ruma::Client::builder() -//! .homeserver_url(homeserver_url) -//! .build::() -//! .await?; -//! -//! let session = client -//! .log_in("@alice:example.com", "secret", None, None) -//! .await?; -//! -//! // You're now logged in! Write the session to a file if you want to restore it later. -//! // Then start using the API! -//! # Result::<(), ruma_client::Error<_, _>>::Ok(()) -//! # }; -//! ``` -//! -//! You can also pass an existing access token to the `Client` constructor to restore a previous -//! session rather than calling `log_in`. This can also be used to create a session for an -//! application service that does not need to log in, but uses the access_token directly: -//! -//! ```no_run -//! # #[cfg(feature = "client-api")] -//! # async { -//! # type HttpClient = ruma_client::http_client::Dummy; -//! let homeserver_url = "https://example.com".to_owned(); -//! let client = ruma_client::Client::builder() -//! .homeserver_url(homeserver_url) -//! .access_token(Some("as_access_token".into())) -//! .build::() -//! .await?; -//! -//! // make calls to the API -//! # Result::<(), ruma_client::Error<_, _>>::Ok(()) -//! # }; -//! ``` -//! -//! The `Client` type also provides methods for registering a new account if you don't already have -//! one with the given homeserver. -//! -//! Beyond these basic convenience methods, `ruma-client` gives you access to the entire Matrix -//! client-server API via the `request` method. You can pass it any of the `Request` types found in -//! `ruma::api::*` and get back a corresponding response from the homeserver. -//! -//! For example: -//! -//! ```no_run -//! # #[cfg(feature = "client-api")] -//! # async { -//! # let homeserver_url = "https://example.com".to_owned(); -//! # let client = ruma_client::Client::builder() -//! # .homeserver_url(homeserver_url) -//! # .build::() -//! # .await?; -//! -//! use ruma_client_api::alias::get_alias; -//! use ruma_common::{api::MatrixVersion, owned_room_alias_id, room_id}; -//! -//! let alias = owned_room_alias_id!("#example_room:example.com"); -//! let response = client.send_request(get_alias::v3::Request::new(alias)).await?; -//! -//! assert_eq!(response.room_id, room_id!("!n8f893n9:example.com")); -//! # Result::<(), ruma_client::Error<_, _>>::Ok(()) -//! # }; -//! ``` -//! -//! # Crate features -//! -//! The following features activate http client types in the [`http_client`] module: -//! -//! * `hyper` -//! * `hyper-native-tls` -//! * `hyper-rustls` -//! * `reqwest` – if you use the `reqwest` library already, activate this feature and configure the -//! TLS backend on `reqwest` directly. If you want to use `reqwest` but don't depend on it -//! already, use one of the sub-features instead. For details on the meaning of these, see -//! [reqwest's documentation](https://docs.rs/reqwest/0.11/reqwest/#optional-features): -//! * `reqwest-native-tls` -//! * `reqwest-native-tls-alpn` -//! * `reqwest-native-tls-vendored` -//! * `reqwest-rustls-manual-roots` -//! * `reqwest-rustls-webpki-roots` -//! * `reqwest-rustls-native-roots` - -#![warn(missing_docs)] -#![cfg_attr(docsrs, feature(doc_auto_cfg))] - -use std::{any::type_name, future::Future}; - -use ruma_common::{ - api::{MatrixVersion, OutgoingRequest, SendAccessToken}, - UserId, -}; -use tracing::{info_span, Instrument}; - -#[cfg(feature = "client-api")] -mod client; -mod error; -pub mod http_client; - -#[cfg(feature = "client-api")] -pub use self::client::{Client, ClientBuilder}; -pub use self::{ - error::Error, - http_client::{DefaultConstructibleHttpClient, HttpClient, HttpClientExt}, -}; - -/// The error type for sending the request `R` with the http client `C`. -pub type ResponseError = - Error<::Error, ::EndpointError>; - -/// The result of sending the request `R` with the http client `C`. -pub type ResponseResult = - Result<::IncomingResponse, ResponseError>; - -fn send_customized_request<'a, C, R, F>( - http_client: &'a C, - homeserver_url: &str, - send_access_token: SendAccessToken<'_>, - for_versions: &[MatrixVersion], - request: R, - customize: F, -) -> impl Future> + Send + 'a -where - C: HttpClient + ?Sized, - R: OutgoingRequest, - F: FnOnce(&mut http::Request) -> Result<(), ResponseError>, -{ - let http_req = - info_span!("serialize_request", request_type = type_name::()).in_scope(move || { - request - .try_into_http_request(homeserver_url, send_access_token, for_versions) - .map_err(ResponseError::::from) - .and_then(|mut req| { - customize(&mut req)?; - Ok(req) - }) - }); - - let send_span = info_span!( - "send_request", - request_type = type_name::(), - http_client = type_name::(), - homeserver_url, - ); - - async move { - let http_res = http_client - .send_http_request(http_req?) - .instrument(send_span) - .await - .map_err(Error::Response)?; - - let res = - info_span!("deserialize_response", response_type = type_name::()) - .in_scope(move || { - ruma_common::api::IncomingResponse::try_from_http_response(http_res) - })?; - - Ok(res) - } -} - -fn add_user_id_to_query( - user_id: &UserId, -) -> impl FnOnce(&mut http::Request) -> Result<(), ResponseError> + '_ { - use assign::assign; - use http::uri::Uri; - - move |http_request| { - let extra_params = serde_html_form::to_string([("user_id", user_id)]).unwrap(); - let uri = http_request.uri_mut(); - let new_path_and_query = match uri.query() { - Some(params) => format!("{}?{params}&{extra_params}", uri.path()), - None => format!("{}?{extra_params}", uri.path()), - }; - *uri = Uri::from_parts(assign!(uri.clone().into_parts(), { - path_and_query: Some(new_path_and_query.parse()?), - }))?; - - Ok(()) - } -} diff --git a/crates/ruma/CHANGELOG.md b/crates/ruma/CHANGELOG.md index c51dab08..098f19e9 100644 --- a/crates/ruma/CHANGELOG.md +++ b/crates/ruma/CHANGELOG.md @@ -1,5 +1,46 @@ # [unreleased] +- The deprecated global `compat` cargo feature was removed. The `compat-*` cargo + features need to be enabled individually. +- The `unstable-unspecified` cargo feature was removed. +- ruma-client is not reexported by ruma anymore, it lives as its own separate + crate. All the corresponding features were removed. +- Bump MSRV to 1.81 + +# 0.12.1 + +Please refer to the changelogs of: + +- ruma-common 0.15.1 +- ruma-events 0.30.1 +- ruma-client-api 0.20.1 +- ruma-appservice-api 0.12.1 + +# 0.12.0 + +- The `unstable-exhaustive-types` cargo feature was replaced by the + `ruma_unstable_exhaustive_types` compile-time `cfg` setting. Like all `cfg` + settings, it can be enabled at compile-time with the `RUSTFLAGS` environment + variable, or inside `.cargo/config.toml`. It can also be enabled by setting + the `RUMA_UNSTABLE_EXHAUSTIVE_TYPES` environment variable. + +Please refer to the changelogs of: + +- ruma-common 0.15.0 +- ruma-events 0.30.0 +- ruma-client-api 0.20.0 +- ruma-push-gateway-api 0.11.0 +- ruma-state-res 0.13.0 + +# 0.11.1 + +Please refer to the changelogs of: + +* ruma-common 0.14.1 +* ruma-events 0.29.1 + +# 0.11.0 + - The `compat-key-id` cargo feature was renamed to `compat-server-signing-key-version`. diff --git a/crates/ruma/Cargo.toml b/crates/ruma/Cargo.toml index 7848369e..8cca9b46 100644 --- a/crates/ruma/Cargo.toml +++ b/crates/ruma/Cargo.toml @@ -17,24 +17,11 @@ all-features = true [features] api = ["ruma-common/api"] canonical-json = ["ruma-common/canonical-json", "ruma-events?/canonical-json"] -client = ["dep:ruma-client"] events = ["dep:ruma-events"] server-util = ["dep:ruma-server-util"] signatures = ["dep:ruma-signatures", "canonical-json"] #state-res = ["dep:ruma-state-res"] -# ruma-client feature flags -client-ext-client-api = ["client", "ruma-client?/client-api"] -client-hyper = ["client", "ruma-client?/hyper"] -client-hyper-native-tls = ["client", "ruma-client?/hyper-native-tls"] -client-reqwest = ["client", "ruma-client?/reqwest"] -client-reqwest-native-tls = ["client", "ruma-client?/reqwest-native-tls"] -client-reqwest-native-tls-alpn = ["client", "ruma-client?/reqwest-native-tls-alpn"] -client-reqwest-native-tls-vendored = ["client", "ruma-client?/reqwest-native-tls-vendored"] -client-reqwest-rustls-manual-roots = ["client", "ruma-client?/reqwest-rustls-manual-roots"] -client-reqwest-rustls-webpki-roots = ["client", "ruma-client?/reqwest-rustls-webpki-roots"] -client-reqwest-rustls-native-roots = ["client", "ruma-client?/reqwest-rustls-native-roots"] - appservice-api-c = [ "api", "events", @@ -115,8 +102,6 @@ html-matrix = ["html", "ruma-html/matrix"] # Everything except compat, js and unstable features full = [ "api", - "client", - "client-ext-client-api", "events", "signatures", #"state-res", @@ -339,7 +324,6 @@ web-time = { workspace = true } ruma-common = { workspace = true } -ruma-client = { workspace = true, optional = true } ruma-events = { workspace = true, optional = true } ruma-html = { workspace = true, optional = true } ruma-server-util = { workspace = true, optional = true } diff --git a/crates/ruma/src/lib.rs b/crates/ruma/src/lib.rs index b61caac0..2669d573 100644 --- a/crates/ruma/src/lib.rs +++ b/crates/ruma/src/lib.rs @@ -64,16 +64,6 @@ //! * `events` //! * `signatures` //! -//! # `ruma-client` features -//! -//! The `client` feature activates [`ruma::client`][client], and `client-ext-client-api` activates -//! `ruma-client`s `client-api` feature. All other `client-*` features activate the same feature -//! without the `client-` prefix on `ruma-client`. See the crate's documentation for the effect of -//! these features. -//! -//! If you are viewing this on `docs.rs`, you can have a look at the feature dependencies by -//! clicking **Feature flags** in the toolbar at the top. -//! //! # Compile-time `cfg` settings //! //! These settings are accepted at compile time to configure the generated code. They can be set as @@ -90,9 +80,6 @@ #![warn(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] -#[cfg(feature = "client")] -#[doc(inline)] -pub use ruma_client as client; #[cfg(feature = "events")] #[doc(inline)] pub use ruma_events as events; @@ -147,7 +134,5 @@ pub use js_int::{ }; #[doc(no_inline)] pub use js_option::JsOption; -#[cfg(feature = "client-ext-client-api")] -pub use ruma_client::Client; pub use ruma_common::*; pub use web_time as time; diff --git a/xtask/src/ci.rs b/xtask/src/ci.rs index a2f44b9a..05380280 100644 --- a/xtask/src/ci.rs +++ b/xtask/src/ci.rs @@ -40,8 +40,6 @@ pub enum CiCmd { Stable, /// Check all crates with all features (stable) StableAll, - /// Check ruma-client without default features (stable) - StableClient, /// Check ruma-common with only the required features (stable) StableCommon, /// Run all tests with almost all features (stable) @@ -112,7 +110,6 @@ impl CiTask { Some(CiCmd::MsrvOwnedIdArc) => self.msrv_owned_id_arc()?, Some(CiCmd::Stable) => self.stable()?, Some(CiCmd::StableAll) => self.stable_all()?, - Some(CiCmd::StableClient) => self.stable_client()?, Some(CiCmd::StableCommon) => self.stable_common()?, Some(CiCmd::TestAll) => self.test_all()?, Some(CiCmd::TestCompat) => self.test_compat()?, @@ -175,7 +172,6 @@ impl CiTask { /// Run all the tasks that use the stable version. fn stable(&self) -> Result<()> { self.stable_all()?; - self.stable_client()?; self.stable_common()?; self.test_all()?; self.test_doc()?; @@ -195,13 +191,6 @@ impl CiTask { .map_err(Into::into) } - /// Check ruma-client without default features with the stable version. - fn stable_client(&self) -> Result<()> { - cmd!(&self.sh, "rustup run stable cargo check -p ruma-client --no-default-features") - .run() - .map_err(Into::into) - } - /// Check ruma-common with onjy the required features with the stable version. fn stable_common(&self) -> Result<()> { cmd!(