diff --git a/crates/ruma-appservice-api/src/event/push_events.rs b/crates/ruma-appservice-api/src/event/push_events.rs index 52f27dc6..9bccfb54 100644 --- a/crates/ruma-appservice-api/src/event/push_events.rs +++ b/crates/ruma-appservice-api/src/event/push_events.rs @@ -70,8 +70,8 @@ pub mod v1 { pub fn try_into_sync_response( self, next_batch: impl Into, - ) -> serde_json::Result { - use ruma_client_api::r0::sync::sync_events; + ) -> serde_json::Result { + use ruma_client_api::sync::sync_events; use ruma_identifiers::RoomId; use serde::Deserialize; use tracing::warn; @@ -81,7 +81,7 @@ pub mod v1 { room_id: Option>, } - let mut response = sync_events::Response::new(next_batch.into()); + let mut response = sync_events::v3::Response::new(next_batch.into()); for raw_event in self.events { let helper = raw_event.deserialize_as::()?; @@ -110,7 +110,7 @@ pub mod v1 { #[cfg(test)] mod helper_tests { use super::{AnyRoomEvent, IncomingRequest, Raw}; - use ruma_client_api::r0::sync::sync_events; + use ruma_client_api::sync::sync_events; use ruma_identifiers::room_id; use serde_json::json; diff --git a/crates/ruma-client-api/src/r0/account.rs b/crates/ruma-client-api/src/account.rs similarity index 100% rename from crates/ruma-client-api/src/r0/account.rs rename to crates/ruma-client-api/src/account.rs diff --git a/crates/ruma-client-api/src/account/add_3pid.rs b/crates/ruma-client-api/src/account/add_3pid.rs new file mode 100644 index 00000000..63e2b4dd --- /dev/null +++ b/crates/ruma-client-api/src/account/add_3pid.rs @@ -0,0 +1,56 @@ +//! `POST /_matrix/client/*/account/3pid/add` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3account3pidadd + + use ruma_api::ruma_api; + use ruma_identifiers::{ClientSecret, SessionId}; + + use crate::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; + + ruma_api! { + metadata: { + description: "Add contact information to a user's account", + method: POST, + name: "add_3pid", + r0_path: "/_matrix/client/r0/account/3pid/add", + stable_path: "/_matrix/client/v3/account/3pid/add", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// Additional information for the User-Interactive Authentication API. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth: Option>, + + /// Client-generated secret string used to protect this session. + pub client_secret: &'a ClientSecret, + + /// The session identifier given by the identity server. + pub sid: &'a SessionId, + } + + #[derive(Default)] + response: {} + + error: UiaaResponse + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given client secret and session identifier. + pub fn new(client_secret: &'a ClientSecret, sid: &'a SessionId) -> Self { + Self { auth: None, client_secret, sid } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/account/bind_3pid.rs b/crates/ruma-client-api/src/account/bind_3pid.rs new file mode 100644 index 00000000..b214f868 --- /dev/null +++ b/crates/ruma-client-api/src/account/bind_3pid.rs @@ -0,0 +1,62 @@ +//! `POST /_matrix/client/*/account/3pid/bind` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3account3pidbind + + use ruma_api::ruma_api; + use ruma_identifiers::{ClientSecret, SessionId}; + + use crate::account::{IdentityServerInfo, IncomingIdentityServerInfo}; + + ruma_api! { + metadata: { + description: "Bind a 3PID to a user's account on an identity server", + method: POST, + name: "bind_3pid", + r0_path: "/_matrix/client/r0/account/3pid/bind", + stable_path: "/_matrix/client/v3/account/3pid/bind", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// Client-generated secret string used to protect this session. + pub client_secret: &'a ClientSecret, + + /// The ID server to send the onward request to as a hostname with an + /// appended colon and port number if the port is not the default. + #[serde(flatten)] + pub identity_server_info: IdentityServerInfo<'a>, + + /// The session identifier given by the identity server. + pub sid: &'a SessionId, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given client secret, identity server information and + /// session identifier. + pub fn new( + client_secret: &'a ClientSecret, + identity_server_info: IdentityServerInfo<'a>, + sid: &'a SessionId, + ) -> Self { + Self { client_secret, identity_server_info, sid } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/account/change_password.rs b/crates/ruma-client-api/src/account/change_password.rs new file mode 100644 index 00000000..d6974c25 --- /dev/null +++ b/crates/ruma-client-api/src/account/change_password.rs @@ -0,0 +1,62 @@ +//! `POST /_matrix/client/*/account/password` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3accountpassword + + use ruma_api::ruma_api; + + use crate::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; + + ruma_api! { + metadata: { + description: "Change the password of the current user's account.", + method: POST, + name: "change_password", + r0_path: "/_matrix/client/r0/account/password", + stable_path: "/_matrix/client/v3/account/password", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The new password for the account. + pub new_password: &'a str, + + /// True to revoke the user's other access tokens, and their associated devices if the + /// request succeeds. + /// + /// Defaults to true. + /// + /// When false, the server can still take advantage of the soft logout method for the user's + /// remaining devices. + #[serde(default = "ruma_serde::default_true", skip_serializing_if = "ruma_serde::is_true")] + pub logout_devices: bool, + + /// Additional authentication information for the user-interactive authentication API. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth: Option>, + } + + #[derive(Default)] + response: {} + + error: UiaaResponse + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given password. + pub fn new(new_password: &'a str) -> Self { + Self { new_password, logout_devices: true, auth: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/account/check_registration_token_validity.rs b/crates/ruma-client-api/src/account/check_registration_token_validity.rs new file mode 100644 index 00000000..7457bb60 --- /dev/null +++ b/crates/ruma-client-api/src/account/check_registration_token_validity.rs @@ -0,0 +1,49 @@ +//! `GET /_matrix/client/*/register/m.login.registration_token/validity` + +pub mod v1 { + //! `/v1/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv1registermloginregistration_tokenvalidity + + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "Checks to see if the given registration token is valid.", + method: GET, + name: "check_registration_token_validity", + unstable_path: "/_matrix/client/unstable/org.matrix.msc3231/register/org.matrix.msc3231.login.registration_token/validity", + stable_path: "/_matrix/client/v1/register/m.login.registration_token/validity", + rate_limited: true, + authentication: None, + added: 1.2, + } + + request: { + /// The registration token to check the validity of. + #[ruma_api(query)] + pub registration_token: &'a str, + } + + response: { + /// A flag to indicate that the registration token is valid. + pub valid: bool, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given registration token. + pub fn new(registration_token: &'a str) -> Self { + Self { registration_token } + } + } + + impl Response { + /// Creates a new `Response` with the given validity flag. + pub fn new(valid: bool) -> Self { + Self { valid } + } + } +} diff --git a/crates/ruma-client-api/src/account/deactivate.rs b/crates/ruma-client-api/src/account/deactivate.rs new file mode 100644 index 00000000..32ff7f7a --- /dev/null +++ b/crates/ruma-client-api/src/account/deactivate.rs @@ -0,0 +1,60 @@ +//! `POST /_matrix/client/*/account/deactivate` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3accountdeactivate + + use ruma_api::ruma_api; + + use crate::{ + account::ThirdPartyIdRemovalStatus, + uiaa::{AuthData, IncomingAuthData, UiaaResponse}, + }; + + ruma_api! { + metadata: { + description: "Deactivate the current user's account.", + method: POST, + name: "deactivate", + r0_path: "/_matrix/client/r0/account/deactivate", + stable_path: "/_matrix/client/v3/account/deactivate", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: { + /// Additional authentication information for the user-interactive authentication API. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth: Option>, + + /// Identity server from which to unbind the user's third party + /// identifier. + #[serde(skip_serializing_if = "Option::is_none")] + pub id_server: Option<&'a str>, + } + + response: { + /// Result of unbind operation. + pub id_server_unbind_result: ThirdPartyIdRemovalStatus, + } + + error: UiaaResponse + } + + impl Request<'_> { + /// Creates an empty `Request`. + pub fn new() -> Self { + Default::default() + } + } + + impl Response { + /// Creates a new `Response` with the given unbind result. + pub fn new(id_server_unbind_result: ThirdPartyIdRemovalStatus) -> Self { + Self { id_server_unbind_result } + } + } +} diff --git a/crates/ruma-client-api/src/account/delete_3pid.rs b/crates/ruma-client-api/src/account/delete_3pid.rs new file mode 100644 index 00000000..4b1146f2 --- /dev/null +++ b/crates/ruma-client-api/src/account/delete_3pid.rs @@ -0,0 +1,51 @@ +//! POST /_matrix/client/*/account/3pid/delete + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3account3piddelete + + use ruma_api::ruma_api; + use ruma_common::thirdparty::Medium; + + use crate::account::ThirdPartyIdRemovalStatus; + + ruma_api! { + metadata: { + description: "Delete a 3PID from a user's account on an identity server.", + method: POST, + name: "delete_3pid", + r0_path: "/_matrix/client/r0/account/3pid/delete", + stable_path: "/_matrix/client/v3/account/3pid/delete", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// Identity server to delete from. + #[serde(skip_serializing_if = "Option::is_none")] + pub id_server: Option<&'a str>, + + /// Medium of the 3PID to be removed. + pub medium: Medium, + + /// Third-party address being removed. + pub address: &'a str, + } + + response: { + /// Result of unbind operation. + pub id_server_unbind_result: ThirdPartyIdRemovalStatus, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given medium and address. + pub fn new(medium: Medium, address: &'a str) -> Self { + Self { id_server: None, medium, address } + } + } +} diff --git a/crates/ruma-client-api/src/account/get_3pids.rs b/crates/ruma-client-api/src/account/get_3pids.rs new file mode 100644 index 00000000..568380d1 --- /dev/null +++ b/crates/ruma-client-api/src/account/get_3pids.rs @@ -0,0 +1,48 @@ +//! `GET /_matrix/client/*/account/3pid` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3account3pid + + use ruma_api::ruma_api; + use ruma_common::thirdparty::ThirdPartyIdentifier; + + ruma_api! { + metadata: { + description: "Get a list of 3rd party contacts associated with the user's account.", + method: GET, + name: "get_3pids", + r0_path: "/_matrix/client/r0/account/3pid", + stable_path: "/_matrix/client/v3/account/3pid", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// A list of third party identifiers the homeserver has associated with the user's account. + #[serde(default)] + #[cfg_attr(not(feature = "compat"), serde(skip_serializing_if = "Vec::is_empty"))] + pub threepids: Vec, + } + + error: crate::Error + } + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given third party identifiers. + pub fn new(threepids: Vec) -> Self { + Self { threepids } + } + } +} diff --git a/crates/ruma-client-api/src/account/get_username_availability.rs b/crates/ruma-client-api/src/account/get_username_availability.rs new file mode 100644 index 00000000..6b79aa67 --- /dev/null +++ b/crates/ruma-client-api/src/account/get_username_availability.rs @@ -0,0 +1,50 @@ +//! `GET /_matrix/client/*/register/available` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3registeravailable + + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "Checks to see if a username is available, and valid, for the server.", + method: GET, + name: "get_username_availability", + r0_path: "/_matrix/client/r0/register/available", + stable_path: "/_matrix/client/v3/register/available", + rate_limited: true, + authentication: None, + added: 1.0, + } + + request: { + /// The username to check the availability of. + #[ruma_api(query)] + pub username: &'a str, + } + + response: { + /// A flag to indicate that the username is available. + /// This should always be true when the server replies with 200 OK. + pub available: bool, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given username. + pub fn new(username: &'a str) -> Self { + Self { username } + } + } + + impl Response { + /// Creates a new `Response` with the given availability flag. + pub fn new(available: bool) -> Self { + Self { available } + } + } +} diff --git a/crates/ruma-client-api/src/account/register.rs b/crates/ruma-client-api/src/account/register.rs new file mode 100644 index 00000000..d8ac3bfa --- /dev/null +++ b/crates/ruma-client-api/src/account/register.rs @@ -0,0 +1,148 @@ +//! `POST /_matrix/client/*/register` + +use serde::{Deserialize, Serialize}; + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3register + + use ruma_api::ruma_api; + use ruma_identifiers::{DeviceId, UserId}; + + use super::{LoginType, RegistrationKind}; + use crate::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; + + ruma_api! { + metadata: { + description: "Register an account on this homeserver.", + method: POST, + name: "register", + r0_path: "/_matrix/client/r0/register", + stable_path: "/_matrix/client/v3/register", + rate_limited: true, + authentication: None, + added: 1.0, + } + + #[derive(Default)] + request: { + /// The desired password for the account. + /// + /// May be empty for accounts that should not be able to log in again + /// with a password, e.g., for guest or application service accounts. + #[serde(skip_serializing_if = "Option::is_none")] + pub password: Option<&'a str>, + + /// Localpart of the desired Matrix ID. + /// + /// If omitted, the homeserver MUST generate a Matrix ID local part. + #[serde(skip_serializing_if = "Option::is_none")] + pub username: Option<&'a str>, + + /// ID of the client device. + /// + /// If this does not correspond to a known client device, a new device will be created. + /// The server will auto-generate a device_id if this is not specified. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_id: Option<&'a DeviceId>, + + /// A display name to assign to the newly-created device. + /// + /// Ignored if `device_id` corresponds to a known device. + #[serde(skip_serializing_if = "Option::is_none")] + pub initial_device_display_name: Option<&'a str>, + + /// Additional authentication information for the user-interactive authentication API. + /// + /// Note that this information is not used to define how the registered user should be + /// authenticated, but is instead used to authenticate the register call itself. + /// It should be left empty, or omitted, unless an earlier call returned an response + /// with status code 401. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth: Option>, + + /// Kind of account to register + /// + /// Defaults to `User` if omitted. + #[ruma_api(query)] + #[serde(default, skip_serializing_if = "ruma_serde::is_default")] + pub kind: RegistrationKind, + + /// If `true`, an `access_token` and `device_id` should not be returned + /// from this call, therefore preventing an automatic login. + #[serde(default, skip_serializing_if = "ruma_serde::is_default")] + pub inhibit_login: bool, + + /// Login `type` used by Appservices. + /// + /// Appservices can [bypass the registration flows][admin] entirely by providing their + /// token in the header and setting this login `type` to `m.login.application_service`. + /// + /// [admin]: https://matrix.org/docs/spec/application_service/r0.1.2#server-admin-style-permissions + #[serde(rename = "type", skip_serializing_if = "Option::is_none")] + pub login_type: Option<&'a LoginType>, + } + + response: { + /// An access token for the account. + /// + /// This access token can then be used to authorize other requests. + #[serde(skip_serializing_if = "Option::is_none")] + pub access_token: Option, + + /// The fully-qualified Matrix ID that has been registered. + pub user_id: Box, + + /// ID of the registered device. + /// + /// Will be the same as the corresponding parameter in the request, if one was specified. + pub device_id: Option>, + } + + error: UiaaResponse + } + + impl Request<'_> { + /// Creates a new `Request` with all parameters defaulted. + pub fn new() -> Self { + Default::default() + } + } + + impl Response { + /// Creates a new `Response` with the given user ID. + pub fn new(user_id: Box) -> Self { + Self { access_token: None, user_id, device_id: None } + } + } +} + +/// The kind of account being registered. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[serde(rename_all = "snake_case")] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] +pub enum RegistrationKind { + /// A guest account + /// + /// These accounts may have limited permissions and may not be supported by all servers. + Guest, + + /// A regular user account + User, +} + +impl Default for RegistrationKind { + fn default() -> Self { + Self::User + } +} + +/// The login type. +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] +pub enum LoginType { + /// An appservice-specific login type + #[serde(rename = "m.login.application_service")] + ApplicationService, +} diff --git a/crates/ruma-client-api/src/account/request_3pid_management_token_via_email.rs b/crates/ruma-client-api/src/account/request_3pid_management_token_via_email.rs new file mode 100644 index 00000000..01cb134a --- /dev/null +++ b/crates/ruma-client-api/src/account/request_3pid_management_token_via_email.rs @@ -0,0 +1,81 @@ +//! `POST /_matrix/client/*/account/3pid/email/requestToken` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3account3pidemailrequesttoken + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::{ClientSecret, SessionId}; + + use crate::account::{IdentityServerInfo, IncomingIdentityServerInfo}; + + ruma_api! { + metadata: { + description: "Request a 3PID management token with a 3rd party email.", + method: POST, + name: "request_3pid_management_token_via_email", + r0_path: "/_matrix/client/r0/account/3pid/email/requestToken", + stable_path: "/_matrix/client/v3/account/3pid/email/requestToken", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// Client-generated secret string used to protect this session. + pub client_secret: &'a ClientSecret, + + /// The email address. + pub email: &'a str, + + /// Used to distinguish protocol level retries from requests to re-send the email. + pub send_attempt: UInt, + + /// Return URL for identity server to redirect the client back to. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_link: Option<&'a str>, + + /// Optional identity server hostname and access token. + /// + /// Deprecated since r0.6.0. + #[serde(flatten, skip_serializing_if = "Option::is_none")] + pub identity_server_info: Option>, + } + + response: { + /// The session identifier given by the identity server. + pub sid: Box, + + /// URL to submit validation token to. + /// + /// If omitted, verification happens without client. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will + /// result in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub submit_url: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the client secret, email and send-attempt counter. + pub fn new(client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt) -> Self { + Self { client_secret, email, send_attempt, next_link: None, identity_server_info: None } + } + } + + impl Response { + /// Creates a new `Response` with the given session identifier. + pub fn new(sid: Box) -> Self { + Self { sid, submit_url: None } + } + } +} diff --git a/crates/ruma-client-api/src/account/request_3pid_management_token_via_msisdn.rs b/crates/ruma-client-api/src/account/request_3pid_management_token_via_msisdn.rs new file mode 100644 index 00000000..4f09bd75 --- /dev/null +++ b/crates/ruma-client-api/src/account/request_3pid_management_token_via_msisdn.rs @@ -0,0 +1,96 @@ +//! `POST /_matrix/client/*/account/3pid/msisdn/requestToken` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3account3pidmsisdnrequesttoken + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::{ClientSecret, SessionId}; + + use crate::account::{IdentityServerInfo, IncomingIdentityServerInfo}; + + ruma_api! { + metadata: { + description: "Request a 3PID management token with a phone number.", + method: POST, + name: "request_3pid_management_token_via_msisdn", + r0_path: "/_matrix/client/r0/account/3pid/msisdn/requestToken", + stable_path: "/_matrix/client/v3/account/3pid/msisdn/requestToken", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// Client-generated secret string used to protect this session. + pub client_secret: &'a ClientSecret, + + /// Two-letter ISO 3166 country code for the phone number. + pub country: &'a str, + + /// Phone number to validate. + pub phone_number: &'a str, + + /// Used to distinguish protocol level retries from requests to re-send the SMS. + pub send_attempt: UInt, + + /// Return URL for identity server to redirect the client back to. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_link: Option<&'a str>, + + /// Optional identity server hostname and access token. + /// + /// Deprecated since r0.6.0. + #[serde(flatten, skip_serializing_if = "Option::is_none")] + pub identity_server_info: Option>, + } + + response: { + /// The session identifier given by the identity server. + pub sid: Box, + + /// URL to submit validation token to. + /// + /// If omitted, verification happens without client. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will + /// result in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub submit_url: Option, + } + + error: crate::Error + } + impl<'a> Request<'a> { + /// Creates a new `Request` with the given client secret, country code, phone number and + /// send-attempt counter. + pub fn new( + client_secret: &'a ClientSecret, + country: &'a str, + phone_number: &'a str, + send_attempt: UInt, + ) -> Self { + Self { + client_secret, + country, + phone_number, + send_attempt, + next_link: None, + identity_server_info: None, + } + } + } + + impl Response { + /// Creates a new `Response` with the given session identifier. + pub fn new(sid: Box) -> Self { + Self { sid, submit_url: None } + } + } +} diff --git a/crates/ruma-client-api/src/account/request_openid_token.rs b/crates/ruma-client-api/src/account/request_openid_token.rs new file mode 100644 index 00000000..e51a1dd1 --- /dev/null +++ b/crates/ruma-client-api/src/account/request_openid_token.rs @@ -0,0 +1,69 @@ +//! `POST /_matrix/client/*/user/{userId}/openid/request_token` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3useruseridopenidrequest_token + + use std::time::Duration; + + use ruma_api::ruma_api; + use ruma_common::authentication::TokenType; + use ruma_identifiers::{ServerName, UserId}; + + ruma_api! { + metadata: { + description: "Request an OpenID 1.0 token to verify identity with a third party.", + name: "request_openid_token", + method: POST, + r0_path: "/_matrix/client/r0/user/:user_id/openid/request_token", + stable_path: "/_matrix/client/v3/user/:user_id/openid/request_token", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// User ID of authenticated user. + #[ruma_api(path)] + pub user_id: &'a UserId, + } + + response: { + /// Access token for verifying user's identity. + pub access_token: String, + + /// Access token type. + pub token_type: TokenType, + + /// Homeserver domain for verification of user's identity. + pub matrix_server_name: Box, + + /// Seconds until token expiration. + #[serde(with = "ruma_serde::duration::secs")] + pub expires_in: Duration, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID. + pub fn new(user_id: &'a UserId) -> Self { + Self { user_id } + } + } + + impl Response { + /// Creates a new `Response` with the given access token, token type, server name and + /// expiration duration. + pub fn new( + access_token: String, + token_type: TokenType, + matrix_server_name: Box, + expires_in: Duration, + ) -> Self { + Self { access_token, token_type, matrix_server_name, expires_in } + } + } +} diff --git a/crates/ruma-client-api/src/account/request_password_change_token_via_email.rs b/crates/ruma-client-api/src/account/request_password_change_token_via_email.rs new file mode 100644 index 00000000..5d021784 --- /dev/null +++ b/crates/ruma-client-api/src/account/request_password_change_token_via_email.rs @@ -0,0 +1,82 @@ +//! `POST /_matrix/client/*/account/password/email/requestToken` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3accountpasswordemailrequesttoken + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::{ClientSecret, SessionId}; + + use crate::account::{IdentityServerInfo, IncomingIdentityServerInfo}; + + ruma_api! { + metadata: { + description: "Request that a password change token is sent to the given email address.", + method: POST, + name: "request_password_change_token_via_email", + r0_path: "/_matrix/client/r0/account/password/email/requestToken", + stable_path: "/_matrix/client/v3/account/password/email/requestToken", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// Client-generated secret string used to protect this session. + pub client_secret: &'a ClientSecret, + + /// The email address. + pub email: &'a str, + + /// Used to distinguish protocol level retries from requests to re-send the email. + pub send_attempt: UInt, + + /// Return URL for identity server to redirect the client back to. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_link: Option<&'a str>, + + /// Optional identity server hostname and access token. + /// + /// Deprecated since r0.6.0. + #[serde(flatten, skip_serializing_if = "Option::is_none")] + pub identity_server_info: Option>, + } + + response: { + /// The session identifier given by the identity server. + pub sid: Box, + + /// URL to submit validation token to. + /// + /// If omitted, verification happens without client. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will result + /// in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub submit_url: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given client secret, email address and send-attempt + /// counter. + pub fn new(client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt) -> Self { + Self { client_secret, email, send_attempt, next_link: None, identity_server_info: None } + } + } + + impl Response { + /// Creates a new `Response` with the given session identifier. + pub fn new(sid: Box) -> Self { + Self { sid, submit_url: None } + } + } +} diff --git a/crates/ruma-client-api/src/account/request_password_change_token_via_msisdn.rs b/crates/ruma-client-api/src/account/request_password_change_token_via_msisdn.rs new file mode 100644 index 00000000..d575f553 --- /dev/null +++ b/crates/ruma-client-api/src/account/request_password_change_token_via_msisdn.rs @@ -0,0 +1,82 @@ +//! `POST /_matrix/client/*/account/password/msisdn/requestToken` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3accountpasswordmsisdnrequesttoken + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::{ClientSecret, SessionId}; + + ruma_api! { + metadata: { + description: "Request that a password change token is sent to the given phone number.", + method: POST, + name: "request_password_change_token_via_msisdn", + r0_path: "/_matrix/client/r0/account/password/msisdn/requestToken", + stable_path: "/_matrix/client/v3/account/password/msisdn/requestToken", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// Client-generated secret string used to protect this session. + pub client_secret: &'a ClientSecret, + + /// Two-letter ISO 3166 country code for the phone number. + pub country: &'a str, + + /// Phone number to validate. + pub phone_number: &'a str, + + /// Used to distinguish protocol level retries from requests to re-send the SMS. + pub send_attempt: UInt, + + /// Return URL for identity server to redirect the client back to. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_link: Option<&'a str>, + } + + response: { + /// The session identifier given by the identity server. + pub sid: Box, + + /// URL to submit validation token to. + /// + /// If omitted, verification happens without client. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will result + /// in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub submit_url: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given client secret, country code, phone number and + /// send-attempt counter. + pub fn new( + client_secret: &'a ClientSecret, + country: &'a str, + phone_number: &'a str, + send_attempt: UInt, + ) -> Self { + Self { client_secret, country, phone_number, send_attempt, next_link: None } + } + } + + impl Response { + /// Creates a new `Response` with the given session identifier. + pub fn new(sid: Box) -> Self { + Self { sid, submit_url: None } + } + } +} diff --git a/crates/ruma-client-api/src/account/request_registration_token_via_email.rs b/crates/ruma-client-api/src/account/request_registration_token_via_email.rs new file mode 100644 index 00000000..b21df0ec --- /dev/null +++ b/crates/ruma-client-api/src/account/request_registration_token_via_email.rs @@ -0,0 +1,82 @@ +//! `POST /_matrix/client/*/register/email/requestToken` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3registeremailrequesttoken + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::{ClientSecret, SessionId}; + + use crate::account::{IdentityServerInfo, IncomingIdentityServerInfo}; + + ruma_api! { + metadata: { + description: "Request a registration token with a 3rd party email.", + method: POST, + name: "request_registration_token_via_email", + r0_path: "/_matrix/client/r0/register/email/requestToken", + stable_path: "/_matrix/client/v3/register/email/requestToken", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// Client-generated secret string used to protect this session. + pub client_secret: &'a ClientSecret, + + /// The email address. + pub email: &'a str, + + /// Used to distinguish protocol level retries from requests to re-send the email. + pub send_attempt: UInt, + + /// Return URL for identity server to redirect the client back to. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_link: Option<&'a str>, + + /// Optional identity server hostname and access token. + /// + /// Deprecated since r0.6.0. + #[serde(flatten, skip_serializing_if = "Option::is_none")] + pub identity_server_info: Option>, + } + + response: { + /// The session identifier given by the identity server. + pub sid: Box, + + /// URL to submit validation token to. + /// + /// If omitted, verification happens without client. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will result + /// in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub submit_url: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given client secret, email address and send-attempt + /// counter. + pub fn new(client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt) -> Self { + Self { client_secret, email, send_attempt, next_link: None, identity_server_info: None } + } + } + + impl Response { + /// Creates a new `Response` with the given session identifier. + pub fn new(sid: Box) -> Self { + Self { sid, submit_url: None } + } + } +} diff --git a/crates/ruma-client-api/src/account/request_registration_token_via_msisdn.rs b/crates/ruma-client-api/src/account/request_registration_token_via_msisdn.rs new file mode 100644 index 00000000..1b78ae58 --- /dev/null +++ b/crates/ruma-client-api/src/account/request_registration_token_via_msisdn.rs @@ -0,0 +1,97 @@ +//! `POST /_matrix/client/*/register/msisdn/requestToken` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3registermsisdnrequesttoken + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::{ClientSecret, SessionId}; + + use crate::account::{IdentityServerInfo, IncomingIdentityServerInfo}; + + ruma_api! { + metadata: { + description: "Request a registration token with a phone number.", + method: POST, + name: "request_registration_token_via_msisdn", + r0_path: "/_matrix/client/r0/register/msisdn/requestToken", + stable_path: "/_matrix/client/v3/register/msisdn/requestToken", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// Client-generated secret string used to protect this session. + pub client_secret: &'a ClientSecret, + + /// Two-letter ISO 3166 country code for the phone number. + pub country: &'a str, + + /// Phone number to validate. + pub phone_number: &'a str, + + /// Used to distinguish protocol level retries from requests to re-send the SMS. + pub send_attempt: UInt, + + /// Return URL for identity server to redirect the client back to. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_link: Option<&'a str>, + + /// Optional identity server hostname and access token. + /// + /// Deprecated since r0.6.0. + #[serde(flatten, skip_serializing_if = "Option::is_none")] + pub identity_server_info: Option>, + } + + response: { + /// The session identifier given by the identity server. + pub sid: Box, + + /// URL to submit validation token to. + /// + /// If omitted, verification happens without client. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will result + /// in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub submit_url: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given client secret, country code, phone number and + /// send-attempt counter. + pub fn new( + client_secret: &'a ClientSecret, + country: &'a str, + phone_number: &'a str, + send_attempt: UInt, + ) -> Self { + Self { + client_secret, + country, + phone_number, + send_attempt, + next_link: None, + identity_server_info: None, + } + } + } + + impl Response { + /// Creates a new `Response` with the given session identifier. + pub fn new(sid: Box) -> Self { + Self { sid, submit_url: None } + } + } +} diff --git a/crates/ruma-client-api/src/account/unbind_3pid.rs b/crates/ruma-client-api/src/account/unbind_3pid.rs new file mode 100644 index 00000000..396aed3a --- /dev/null +++ b/crates/ruma-client-api/src/account/unbind_3pid.rs @@ -0,0 +1,58 @@ +//! `POST /_matrix/client/*/account/3pid/unbind` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3account3pidunbind + + use ruma_api::ruma_api; + use ruma_common::thirdparty::Medium; + + use crate::account::ThirdPartyIdRemovalStatus; + + ruma_api! { + metadata: { + description: "Unbind a 3PID from a user's account on an identity server.", + method: POST, + name: "unbind_3pid", + r0_path: "/_matrix/client/r0/account/3pid/unbind", + stable_path: "/_matrix/client/v3/account/3pid/unbind", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// Identity server to unbind from. + #[serde(skip_serializing_if = "Option::is_none")] + pub id_server: Option<&'a str>, + + /// Medium of the 3PID to be removed. + pub medium: Medium, + + /// Third-party address being removed. + pub address: &'a str, + } + + response: { + /// Result of unbind operation. + pub id_server_unbind_result: ThirdPartyIdRemovalStatus, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given medium and third-party address. + pub fn new(medium: Medium, address: &'a str) -> Self { + Self { id_server: None, medium, address } + } + } + + impl Response { + /// Creates a new `Response` with the given unbind result. + pub fn new(id_server_unbind_result: ThirdPartyIdRemovalStatus) -> Self { + Self { id_server_unbind_result } + } + } +} diff --git a/crates/ruma-client-api/src/account/whoami.rs b/crates/ruma-client-api/src/account/whoami.rs new file mode 100644 index 00000000..687d3527 --- /dev/null +++ b/crates/ruma-client-api/src/account/whoami.rs @@ -0,0 +1,47 @@ +//! `GET /_matrix/client/*/account/whoami` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3accountwhoami + + use ruma_api::ruma_api; + use ruma_identifiers::UserId; + + ruma_api! { + metadata: { + description: "Get information about the owner of a given access token.", + method: GET, + name: "whoami", + r0_path: "/_matrix/client/r0/account/whoami", + stable_path: "/_matrix/client/v3/account/whoami", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// The id of the user that owns the access token. + pub user_id: Box, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given user ID. + pub fn new(user_id: Box) -> Self { + Self { user_id } + } + } +} diff --git a/crates/ruma-client-api/src/r0/alias.rs b/crates/ruma-client-api/src/alias.rs similarity index 100% rename from crates/ruma-client-api/src/r0/alias.rs rename to crates/ruma-client-api/src/alias.rs diff --git a/crates/ruma-client-api/src/alias/create_alias.rs b/crates/ruma-client-api/src/alias/create_alias.rs new file mode 100644 index 00000000..d36dd825 --- /dev/null +++ b/crates/ruma-client-api/src/alias/create_alias.rs @@ -0,0 +1,51 @@ +//! `PUT /_matrix/client/*/directory/room/{roomAlias}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3directoryroomroomalias + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomAliasId, RoomId}; + + ruma_api! { + metadata: { + description: "Add an alias to a room.", + method: PUT, + name: "create_alias", + r0_path: "/_matrix/client/r0/directory/room/:room_alias", + stable_path: "/_matrix/client/v3/directory/room/:room_alias", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room alias to set. + #[ruma_api(path)] + pub room_alias: &'a RoomAliasId, + + /// The room ID to set. + pub room_id: &'a RoomId, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room alias and room id. + pub fn new(room_alias: &'a RoomAliasId, room_id: &'a RoomId) -> Self { + Self { room_alias, room_id } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/alias/delete_alias.rs b/crates/ruma-client-api/src/alias/delete_alias.rs new file mode 100644 index 00000000..832b29e7 --- /dev/null +++ b/crates/ruma-client-api/src/alias/delete_alias.rs @@ -0,0 +1,48 @@ +//! `DELETE /_matrix/client/*/directory/room/{roomAlias}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#delete_matrixclientv3directoryroomroomalias + + use ruma_api::ruma_api; + use ruma_identifiers::RoomAliasId; + + ruma_api! { + metadata: { + description: "Remove an alias from a room.", + method: DELETE, + name: "delete_alias", + r0_path: "/_matrix/client/r0/directory/room/:room_alias", + stable_path: "/_matrix/client/v3/directory/room/:room_alias", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room alias to remove. + #[ruma_api(path)] + pub room_alias: &'a RoomAliasId, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room alias. + pub fn new(room_alias: &'a RoomAliasId) -> Self { + Self { room_alias } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/alias/get_alias.rs b/crates/ruma-client-api/src/alias/get_alias.rs new file mode 100644 index 00000000..b678bb2d --- /dev/null +++ b/crates/ruma-client-api/src/alias/get_alias.rs @@ -0,0 +1,53 @@ +//! `GET /_matrix/client/*/directory/room/{roomAlias}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3directoryroomroomalias + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomAliasId, RoomId, ServerName}; + + ruma_api! { + metadata: { + description: "Resolve a room alias to a room ID.", + method: GET, + name: "get_alias", + r0_path: "/_matrix/client/r0/directory/room/:room_alias", + stable_path: "/_matrix/client/v3/directory/room/:room_alias", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// The room alias. + #[ruma_api(path)] + pub room_alias: &'a RoomAliasId, + } + + response: { + /// The room ID for this room alias. + pub room_id: Box, + + /// A list of servers that are aware of this room ID. + pub servers: Vec>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room alias id. + pub fn new(room_alias: &'a RoomAliasId) -> Self { + Self { room_alias } + } + } + + impl Response { + /// Creates a new `Response` with the given room id and servers + pub fn new(room_id: Box, servers: Vec>) -> Self { + Self { room_id, servers } + } + } +} diff --git a/crates/ruma-client-api/src/r0/appservice.rs b/crates/ruma-client-api/src/appservice.rs similarity index 100% rename from crates/ruma-client-api/src/r0/appservice.rs rename to crates/ruma-client-api/src/appservice.rs diff --git a/crates/ruma-client-api/src/appservice/set_room_visibility.rs b/crates/ruma-client-api/src/appservice/set_room_visibility.rs new file mode 100644 index 00000000..77242358 --- /dev/null +++ b/crates/ruma-client-api/src/appservice/set_room_visibility.rs @@ -0,0 +1,57 @@ +//! `PUT /_matrix/client/*/directory/list/appservice/{networkId}/{roomId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/application-service-api/#put_matrixclientv3directorylistappservicenetworkidroomid + + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + use crate::room::Visibility; + + ruma_api! { + metadata: { + description: "Updates the visibility of a given room on the application service's room directory.", + method: PUT, + name: "set_room_visibility", + r0_path: "/_matrix/client/r0/directory/list/appservice/:network_id/:room_id", + stable_path: "/_matrix/client/v3/directory/list/appservice/:network_id/:room_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The protocol (network) ID to update the room list for. + #[ruma_api(path)] + pub network_id: &'a str, + + /// The room ID to add to the directory. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// Whether the room should be visible (public) in the directory or not (private). + pub visibility: Visibility, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given network ID, room ID and visibility. + pub fn new(network_id: &'a str, room_id: &'a RoomId, visibility: Visibility) -> Self { + Self { network_id, room_id, visibility } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0/backup.rs b/crates/ruma-client-api/src/backup.rs similarity index 100% rename from crates/ruma-client-api/src/r0/backup.rs rename to crates/ruma-client-api/src/backup.rs diff --git a/crates/ruma-client-api/src/backup/add_backup_key_session.rs b/crates/ruma-client-api/src/backup/add_backup_key_session.rs new file mode 100644 index 00000000..f48f52e7 --- /dev/null +++ b/crates/ruma-client-api/src/backup/add_backup_key_session.rs @@ -0,0 +1,80 @@ +//! `PUT /_matrix/client/*/room_keys/keys/{roomId}/{sessionId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3room_keyskeysroomidsessionid + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + use ruma_serde::Raw; + + use crate::backup::KeyBackupData; + + ruma_api! { + metadata: { + description: "Store several keys in the backup.", + method: PUT, + name: "add_backup_key_session", + unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id/:session_id", + r0_path: "/_matrix/client/r0/room_keys/keys/:room_id/:session_id", + stable_path: "/_matrix/client/v3/room_keys/keys/:room_id/:session_id", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The backup version. + /// + /// Must be the current backup. + #[ruma_api(query)] + pub version: &'a str, + + /// The ID of the room that the requested key is for. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The ID of the megolm session whose key is requested. + #[ruma_api(path)] + pub session_id: &'a str, + + /// The key information to backup. + #[ruma_api(body)] + pub session_data: Raw, + } + + response: { + /// An opaque string representing stored keys in the backup. + /// + /// Clients can compare it with the etag value they received in the request of their last + /// key storage request. + pub etag: String, + + /// The number of keys stored in the backup. + pub count: UInt, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version, room_id, session_id and session_data. + pub fn new( + version: &'a str, + room_id: &'a RoomId, + session_id: &'a str, + session_data: Raw, + ) -> Self { + Self { version, room_id, session_id, session_data } + } + } + + impl Response { + /// Creates an new `Response` with the given etag and count. + pub fn new(etag: String, count: UInt) -> Self { + Self { etag, count } + } + } +} diff --git a/crates/ruma-client-api/src/backup/add_backup_key_sessions.rs b/crates/ruma-client-api/src/backup/add_backup_key_sessions.rs new file mode 100644 index 00000000..c90c3afa --- /dev/null +++ b/crates/ruma-client-api/src/backup/add_backup_key_sessions.rs @@ -0,0 +1,76 @@ +//! `PUT /_matrix/client/*/room_keys/keys/{roomId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3room_keyskeysroomid + + use std::collections::BTreeMap; + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + use ruma_serde::Raw; + + use crate::backup::KeyBackupData; + + ruma_api! { + metadata: { + description: "Store several sessions in the backup.", + method: PUT, + name: "add_backup_key_sessions", + unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id", + r0_path: "/_matrix/client/r0/room_keys/keys/:room_id", + stable_path: "/_matrix/client/v3/room_keys/keys/:room_id", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The backup version. + /// + /// Must be the current backup. + #[ruma_api(query)] + pub version: &'a str, + + /// The ID of the room that the requested key is for. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// A map from session IDs to key data. + pub sessions: BTreeMap>, + } + + response: { + /// An opaque string representing stored keys in the backup. + /// + /// Clients can compare it with the etag value they received in the request of their last + /// key storage request. + pub etag: String, + + /// The number of keys stored in the backup. + pub count: UInt, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version, room_id and sessions. + pub fn new( + version: &'a str, + room_id: &'a RoomId, + sessions: BTreeMap>, + ) -> Self { + Self { version, room_id, sessions } + } + } + + impl Response { + /// Creates an new `Response` with the given etag and count. + pub fn new(etag: String, count: UInt) -> Self { + Self { etag, count } + } + } +} diff --git a/crates/ruma-client-api/src/backup/add_backup_keys.rs b/crates/ruma-client-api/src/backup/add_backup_keys.rs new file mode 100644 index 00000000..3dc910c0 --- /dev/null +++ b/crates/ruma-client-api/src/backup/add_backup_keys.rs @@ -0,0 +1,66 @@ +//! `PUT /_matrix/client/*/room_keys/keys` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3room_keyskeys + + use std::collections::BTreeMap; + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + use crate::backup::RoomKeyBackup; + + ruma_api! { + metadata: { + description: "Store several keys in the backup.", + method: PUT, + name: "add_backup_keys", + unstable_path: "/_matrix/client/unstable/room_keys/keys", + stable_path: "/_matrix/client/v3/room_keys/keys", + rate_limited: true, + authentication: AccessToken, + added: 1.1, + } + + request: { + /// The backup version. + /// + /// Must be the current backup. + #[ruma_api(query)] + pub version: &'a str, + + /// A map from room IDs to session IDs to key data. + pub rooms: BTreeMap, RoomKeyBackup>, + } + + response: { + /// An opaque string representing stored keys in the backup. + /// + /// Clients can compare it with the etag value they received in the request of their last + /// key storage request. + pub etag: String, + + /// The number of keys stored in the backup. + pub count: UInt, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version and room key backups. + pub fn new(version: &'a str, rooms: BTreeMap, RoomKeyBackup>) -> Self { + Self { version, rooms } + } + } + + impl Response { + /// Creates a new `Response` with the given etag and key count. + pub fn new(etag: String, count: UInt) -> Self { + Self { etag, count } + } + } +} diff --git a/crates/ruma-client-api/src/backup/create_backup.rs b/crates/ruma-client-api/src/backup/create_backup.rs new file mode 100644 index 00000000..bcb6996b --- /dev/null +++ b/crates/ruma-client-api/src/backup/create_backup.rs @@ -0,0 +1,52 @@ +//! `POST /_matrix/client/*/room_keys/version` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3room_keysversion + + use ruma_api::ruma_api; + use ruma_serde::Raw; + + use crate::backup::BackupAlgorithm; + + ruma_api! { + metadata: { + description: "Creates a new backup.", + method: POST, + name: "create_backup", + unstable_path: "/_matrix/client/unstable/room_keys/version", + stable_path: "/_matrix/client/v3/room_keys/version", + rate_limited: true, + authentication: AccessToken, + added: 1.1, + } + + request: { + /// The algorithm used for storing backups. + #[ruma_api(body)] + pub algorithm: Raw, + } + + response: { + /// The backup version. + pub version: String, + } + + error: crate::Error + } + + impl Request { + /// Creates a new `Request` with the given backup algorithm. + pub fn new(algorithm: Raw) -> Self { + Self { algorithm } + } + } + + impl Response { + /// Creates a new `Response` with the given version. + pub fn new(version: String) -> Self { + Self { version } + } + } +} diff --git a/crates/ruma-client-api/src/backup/delete_backup.rs b/crates/ruma-client-api/src/backup/delete_backup.rs new file mode 100644 index 00000000..bdb9d816 --- /dev/null +++ b/crates/ruma-client-api/src/backup/delete_backup.rs @@ -0,0 +1,48 @@ +//! `DELETE /_matrix/client/*/room_keys/version/{version}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#delete_matrixclientv3room_keysversionversion + + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "Delete an existing backup.", + method: DELETE, + name: "delete_backup", + unstable_path: "/_matrix/client/unstable/room_keys/version/:version", + r0_path: "/_matrix/client/r0/room_keys/version/:version", + stable_path: "/_matrix/client/v3/room_keys/version/:version", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The backup version. + #[ruma_api(path)] + pub version: &'a str, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version, room_id and sessions. + pub fn new(version: &'a str) -> Self { + Self { version } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/backup/delete_backup_key_session.rs b/crates/ruma-client-api/src/backup/delete_backup_key_session.rs new file mode 100644 index 00000000..3c83e7a7 --- /dev/null +++ b/crates/ruma-client-api/src/backup/delete_backup_key_session.rs @@ -0,0 +1,68 @@ +//! `DELETE /_matrix/client/*/room_keys/keys/{roomId}/{sessionId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#delete_matrixclientv3room_keyskeysroomidsessionid + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + ruma_api! { + metadata: { + description: "Delete a key from the backup", + method: DELETE, + name: "delete_backup_key_session", + unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id/:session_id", + r0_path: "/_matrix/client/r0/room_keys/keys/:room_id/:session_id", + stable_path: "/_matrix/client/v3/room_keys/keys/:room_id/:session_id", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The backup version. + /// + /// Must be the current backup. + #[ruma_api(query)] + pub version: &'a str, + + /// The ID of the room that the requested key is for. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The ID of the megolm session whose key is requested. + #[ruma_api(path)] + pub session_id: &'a str, + } + + response: { + /// An opaque string representing stored keys in the backup. + /// + /// Clients can compare it with the etag value they received in the request of their last + /// key storage request. + pub etag: String, + + /// The number of keys stored in the backup. + pub count: UInt, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version, room_id and session_id. + pub fn new(version: &'a str, room_id: &'a RoomId, session_id: &'a str) -> Self { + Self { version, room_id, session_id } + } + } + + impl Response { + /// Creates an new `Response` with the given etag and count. + pub fn new(etag: String, count: UInt) -> Self { + Self { etag, count } + } + } +} diff --git a/crates/ruma-client-api/src/backup/delete_backup_key_sessions.rs b/crates/ruma-client-api/src/backup/delete_backup_key_sessions.rs new file mode 100644 index 00000000..d82a0455 --- /dev/null +++ b/crates/ruma-client-api/src/backup/delete_backup_key_sessions.rs @@ -0,0 +1,65 @@ +//! `DELETE /_matrix/client/*/room_keys/keys/{roomId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#delete_matrixclientv3room_keyskeysroomid + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + ruma_api! { + metadata: { + description: "Delete keys from the backup for a given room.", + method: DELETE, + name: "delete_backup_key_sessions", + unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id", + r0_path: "/_matrix/client/r0/room_keys/keys/:room_id", + stable_path: "/_matrix/client/v3/room_keys/keys/:room_id", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The backup version. + /// + /// Must be the current backup. + #[ruma_api(query)] + pub version: &'a str, + + /// The ID of the room that the requested key is for. + #[ruma_api(path)] + pub room_id: &'a RoomId, + } + + response: { + /// An opaque string representing stored keys in the backup. + /// + /// Clients can compare it with the etag value they received in the request of their last + /// key storage request. + pub etag: String, + + /// The number of keys stored in the backup. + pub count: UInt, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version and room_id. + + pub fn new(version: &'a str, room_id: &'a RoomId) -> Self { + Self { version, room_id } + } + } + + impl Response { + /// Creates an new `Response` with the given etag and count. + pub fn new(etag: String, count: UInt) -> Self { + Self { etag, count } + } + } +} diff --git a/crates/ruma-client-api/src/backup/delete_backup_keys.rs b/crates/ruma-client-api/src/backup/delete_backup_keys.rs new file mode 100644 index 00000000..7051141b --- /dev/null +++ b/crates/ruma-client-api/src/backup/delete_backup_keys.rs @@ -0,0 +1,59 @@ +//! `DELETE /_matrix/client/*/room_keys/keys` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#delete_matrixclientv3room_keyskeys + + use js_int::UInt; + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "Delete all keys in a backup.", + method: DELETE, + name: "delete_backup_keys", + unstable_path: "/_matrix/client/unstable/room_keys/keys", + r0_path: "/_matrix/client/r0/room_keys/keys", + stable_path: "/_matrix/client/v3/room_keys/keys", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The backup version. + /// + /// Must be the current backup. + #[ruma_api(query)] + pub version: &'a str, + } + + response: { + /// An opaque string representing stored keys in the backup. + /// + /// Clients can compare it with the etag value they received in the request of their last + /// key storage request. + pub etag: String, + + /// The number of keys stored in the backup. + pub count: UInt, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version. + pub fn new(version: &'a str) -> Self { + Self { version } + } + } + + impl Response { + /// Creates an new `Response` with the given etag and count. + pub fn new(etag: String, count: UInt) -> Self { + Self { etag, count } + } + } +} diff --git a/crates/ruma-client-api/src/backup/get_backup.rs b/crates/ruma-client-api/src/backup/get_backup.rs new file mode 100644 index 00000000..6e0bc1f4 --- /dev/null +++ b/crates/ruma-client-api/src/backup/get_backup.rs @@ -0,0 +1,134 @@ +//! `GET /_matrix/client/*/room_keys/version/{version}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3room_keysversionversion + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_serde::Raw; + use serde::{ser, Deserialize, Deserializer, Serialize}; + use serde_json::value::{to_raw_value as to_raw_json_value, RawValue as RawJsonValue}; + + use crate::backup::BackupAlgorithm; + + ruma_api! { + metadata: { + description: "Get information about an existing backup.", + method: GET, + name: "get_backup", + unstable_path: "/_matrix/client/unstable/room_keys/version/:version", + stable_path: "/_matrix/client/v3/room_keys/version/:version", + rate_limited: true, + authentication: AccessToken, + added: 1.1, + } + + request: { + /// The backup version. + #[ruma_api(path)] + pub version: &'a str, + } + + #[ruma_api(manual_body_serde)] + response: { + /// The algorithm used for storing backups. + pub algorithm: Raw, + + /// The number of keys stored in the backup. + pub count: UInt, + + /// An opaque string representing stored keys in the backup. + /// + /// Clients can compare it with the etag value they received in the request of their last + /// key storage request. + pub etag: String, + + /// The backup version. + pub version: String, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version. + pub fn new(version: &'a str) -> Self { + Self { version } + } + } + + impl Response { + /// Creates a new `Response` with the gien algorithm, key count, etag and version. + pub fn new( + algorithm: Raw, + count: UInt, + etag: String, + version: String, + ) -> Self { + Self { algorithm, count, etag, version } + } + } + + #[derive(Deserialize)] + pub(in crate::backup) struct ResponseBodyRepr { + pub algorithm: Box, + pub auth_data: Box, + pub count: UInt, + pub etag: String, + pub version: String, + } + + #[derive(Serialize)] + pub(in crate::backup) struct RefResponseBodyRepr<'a> { + pub algorithm: &'a RawJsonValue, + pub auth_data: &'a RawJsonValue, + pub count: UInt, + pub etag: &'a str, + pub version: &'a str, + } + + #[derive(Deserialize, Serialize)] + pub(in crate::backup) struct AlgorithmWithData { + pub algorithm: Box, + pub auth_data: Box, + } + + impl<'de> Deserialize<'de> for ResponseBody { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let ResponseBodyRepr { algorithm, auth_data, count, etag, version } = + ResponseBodyRepr::deserialize(deserializer)?; + + let algorithm = Raw::from_json( + to_raw_json_value(&AlgorithmWithData { algorithm, auth_data }).unwrap(), + ); + + Ok(Self { algorithm, count, etag, version }) + } + } + + impl Serialize for ResponseBody { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let ResponseBody { algorithm, count, etag, version } = self; + let AlgorithmWithData { algorithm, auth_data } = + algorithm.deserialize_as().map_err(ser::Error::custom)?; + + let repr = RefResponseBodyRepr { + algorithm: &algorithm, + auth_data: &auth_data, + count: *count, + etag, + version, + }; + + repr.serialize(serializer) + } + } +} diff --git a/crates/ruma-client-api/src/backup/get_backup_key_session.rs b/crates/ruma-client-api/src/backup/get_backup_key_session.rs new file mode 100644 index 00000000..81de9671 --- /dev/null +++ b/crates/ruma-client-api/src/backup/get_backup_key_session.rs @@ -0,0 +1,65 @@ +//! `GET /_matrix/client/*/room_keys/keys/{roomId}/{sessionId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3room_keyskeysroomidsessionid + + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + use ruma_serde::Raw; + + use crate::backup::KeyBackupData; + + ruma_api! { + metadata: { + description: "Retrieve a key from the backup", + method: GET, + name: "get_backup_key_session", + unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id/:session_id", + r0_path: "/_matrix/client/r0/room_keys/keys/:room_id/:session_id", + stable_path: "/_matrix/client/v3/room_keys/keys/:room_id/:session_id", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The backup version. + /// + /// Must be the current backup. + #[ruma_api(query)] + pub version: &'a str, + + /// The ID of the room that the requested key is for. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The ID of the megolm session whose key is requested. + #[ruma_api(path)] + pub session_id: &'a str, + } + + response: { + /// Information about the requested backup key. + #[ruma_api(body)] + pub key_data: Raw, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version, room_id and session_id. + pub fn new(version: &'a str, room_id: &'a RoomId, session_id: &'a str) -> Self { + Self { version, room_id, session_id } + } + } + + impl Response { + /// Creates a new `Response` with the given key_data. + pub fn new(key_data: Raw) -> Self { + Self { key_data } + } + } +} diff --git a/crates/ruma-client-api/src/backup/get_backup_key_sessions.rs b/crates/ruma-client-api/src/backup/get_backup_key_sessions.rs new file mode 100644 index 00000000..42824ea1 --- /dev/null +++ b/crates/ruma-client-api/src/backup/get_backup_key_sessions.rs @@ -0,0 +1,62 @@ +//! `GET /_matrix/client/*/room_keys/keys/{roomId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3room_keyskeysroomid + + use std::collections::BTreeMap; + + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + use ruma_serde::Raw; + + use crate::backup::KeyBackupData; + + ruma_api! { + metadata: { + description: "Retrieve sessions from the backup for a given room.", + method: GET, + name: "get_backup_key_sessions", + unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id", + r0_path: "/_matrix/client/r0/room_keys/keys/:room_id", + stable_path: "/_matrix/client/v3/room_keys/keys/:room_id", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The backup version + /// + /// Must be the current backup. + #[ruma_api(query)] + pub version: &'a str, + + /// The ID of the room that the requested key is for. + #[ruma_api(path)] + pub room_id: &'a RoomId, + } + + response: { + /// A map of session IDs to key data. + pub sessions: BTreeMap>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version and room_id. + pub fn new(version: &'a str, room_id: &'a RoomId) -> Self { + Self { version, room_id } + } + } + + impl Response { + /// Creates a new `Response` with the given sessions. + pub fn new(sessions: BTreeMap>) -> Self { + Self { sessions } + } + } +} diff --git a/crates/ruma-client-api/src/backup/get_backup_keys.rs b/crates/ruma-client-api/src/backup/get_backup_keys.rs new file mode 100644 index 00000000..c8d9a6d5 --- /dev/null +++ b/crates/ruma-client-api/src/backup/get_backup_keys.rs @@ -0,0 +1,57 @@ +//! `GET /_matrix/client/*/room_keys/keys` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3room_keyskeys + + use std::collections::BTreeMap; + + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + use crate::backup::RoomKeyBackup; + + ruma_api! { + metadata: { + description: "Retrieve all keys from a backup.", + method: GET, + name: "get_backup_keys", + unstable_path: "/_matrix/client/unstable/room_keys/keys", + r0_path: "/_matrix/client/r0/room_keys/keys", + stable_path: "/_matrix/client/v3/room_keys/keys", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The backup version. + /// + /// Must be the current backup. + #[ruma_api(query)] + pub version: &'a str, + } + + response: { + /// A map from room IDs to session IDs to key data. + pub rooms: BTreeMap, RoomKeyBackup>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given version. + pub fn new(version: &'a str) -> Self { + Self { version } + } + } + + impl Response { + /// Creates a new `Response` with the given room key backups. + pub fn new(rooms: BTreeMap, RoomKeyBackup>) -> Self { + Self { rooms } + } + } +} diff --git a/crates/ruma-client-api/src/backup/get_latest_backup.rs b/crates/ruma-client-api/src/backup/get_latest_backup.rs new file mode 100644 index 00000000..8db9e4b2 --- /dev/null +++ b/crates/ruma-client-api/src/backup/get_latest_backup.rs @@ -0,0 +1,111 @@ +//! `GET /_matrix/client/*/room_keys/version` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3room_keysversion + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_serde::Raw; + use serde::{ser, Deserialize, Deserializer, Serialize}; + use serde_json::value::to_raw_value as to_raw_json_value; + + use crate::backup::{ + get_backup::v3::{AlgorithmWithData, RefResponseBodyRepr, ResponseBodyRepr}, + BackupAlgorithm, + }; + + ruma_api! { + metadata: { + description: "Get information about the latest backup.", + method: GET, + name: "get_latest_backup", + unstable_path: "/_matrix/client/unstable/room_keys/version", + r0_path: "/_matrix/client/r0/room_keys/version", + stable_path: "/_matrix/client/v3/room_keys/version", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + #[ruma_api(manual_body_serde)] + response: { + /// The algorithm used for storing backups. + pub algorithm: Raw, + + /// The number of keys stored in the backup. + pub count: UInt, + + /// An opaque string representing stored keys in the backup. + /// + /// Clients can compare it with the etag value they received in the request of their last + /// key storage request. + pub etag: String, + + /// The backup version. + pub version: String, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given algorithm, key count, etag and version. + pub fn new( + algorithm: Raw, + count: UInt, + etag: String, + version: String, + ) -> Self { + Self { algorithm, count, etag, version } + } + } + + impl<'de> Deserialize<'de> for ResponseBody { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let ResponseBodyRepr { algorithm, auth_data, count, etag, version } = + ResponseBodyRepr::deserialize(deserializer)?; + + let algorithm = Raw::from_json( + to_raw_json_value(&AlgorithmWithData { algorithm, auth_data }).unwrap(), + ); + + Ok(Self { algorithm, count, etag, version }) + } + } + + impl Serialize for ResponseBody { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let ResponseBody { algorithm, count, etag, version } = self; + let AlgorithmWithData { algorithm, auth_data } = + algorithm.deserialize_as().map_err(ser::Error::custom)?; + + let repr = RefResponseBodyRepr { + algorithm: &algorithm, + auth_data: &auth_data, + count: *count, + etag, + version, + }; + + repr.serialize(serializer) + } + } +} diff --git a/crates/ruma-client-api/src/backup/update_backup.rs b/crates/ruma-client-api/src/backup/update_backup.rs new file mode 100644 index 00000000..89530802 --- /dev/null +++ b/crates/ruma-client-api/src/backup/update_backup.rs @@ -0,0 +1,54 @@ +//! `POST /_matrix/client/*/room_keys/version` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3room_keysversion + + use ruma_api::ruma_api; + use ruma_serde::Raw; + + use crate::backup::BackupAlgorithm; + + ruma_api! { + metadata: { + description: "Update information about an existing backup.", + method: POST, + name: "update_backup", + unstable_path: "/_matrix/client/unstable/room_keys/version/:version", + stable_path: "/_matrix/client/v3/room_keys/version/:version", + rate_limited: true, + authentication: AccessToken, + added: 1.1, + } + + request: { + /// The backup version. + #[ruma_api(path)] + pub version: &'a str, + + /// The algorithm used for storing backups. + #[ruma_api(body)] + pub algorithm: Raw, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given backup version and algorithm. + pub fn new(version: &'a str, algorithm: Raw) -> Self { + Self { version, algorithm } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0/capabilities.rs b/crates/ruma-client-api/src/capabilities.rs similarity index 99% rename from crates/ruma-client-api/src/r0/capabilities.rs rename to crates/ruma-client-api/src/capabilities.rs index d5bebe93..258c697e 100644 --- a/crates/ruma-client-api/src/r0/capabilities.rs +++ b/crates/ruma-client-api/src/capabilities.rs @@ -2,14 +2,13 @@ use std::{borrow::Cow, collections::BTreeMap}; +use iter::{CapabilitiesIter, CapabilityRef}; use maplit::btreemap; use ruma_identifiers::RoomVersionId; use ruma_serde::StringEnum; use serde::{Deserialize, Serialize}; use serde_json::{from_value as from_json_value, to_value as to_json_value, Value as JsonValue}; -use iter::{CapabilitiesIter, CapabilityRef}; - use crate::PrivOwnedStr; pub mod get_capabilities; diff --git a/crates/ruma-client-api/src/capabilities/get_capabilities.rs b/crates/ruma-client-api/src/capabilities/get_capabilities.rs new file mode 100644 index 00000000..2d17a0d7 --- /dev/null +++ b/crates/ruma-client-api/src/capabilities/get_capabilities.rs @@ -0,0 +1,54 @@ +//! `GET /_matrix/client/*/capabilities` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3capabilities + + use ruma_api::ruma_api; + + use crate::capabilities::Capabilities; + + ruma_api! { + metadata: { + description: "Gets information about the server's supported feature set and other relevant capabilities.", + method: GET, + name: "get_capabilities", + r0_path: "/_matrix/client/r0/capabilities", + stable_path: "/_matrix/client/v3/capabilities", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// The capabilities the server supports + pub capabilities: Capabilities, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given capabilities. + pub fn new(capabilities: Capabilities) -> Self { + Self { capabilities } + } + } + + impl From for Response { + fn from(capabilities: Capabilities) -> Self { + Self::new(capabilities) + } + } +} diff --git a/crates/ruma-client-api/src/r0/capabilities/iter.rs b/crates/ruma-client-api/src/capabilities/iter.rs similarity index 100% rename from crates/ruma-client-api/src/r0/capabilities/iter.rs rename to crates/ruma-client-api/src/capabilities/iter.rs diff --git a/crates/ruma-client-api/src/r0/config.rs b/crates/ruma-client-api/src/config.rs similarity index 100% rename from crates/ruma-client-api/src/r0/config.rs rename to crates/ruma-client-api/src/config.rs diff --git a/crates/ruma-client-api/src/config/get_global_account_data.rs b/crates/ruma-client-api/src/config/get_global_account_data.rs new file mode 100644 index 00000000..1725e405 --- /dev/null +++ b/crates/ruma-client-api/src/config/get_global_account_data.rs @@ -0,0 +1,59 @@ +//! `GET /_matrix/client/*/user/{userId}/account_data/{type}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3useruseridaccount_datatype + + use ruma_api::ruma_api; + use ruma_events::AnyGlobalAccountDataEventContent; + use ruma_identifiers::UserId; + use ruma_serde::Raw; + + ruma_api! { + metadata: { + description: "Gets global account data for a user.", + name: "get_global_account_data", + method: GET, + r0_path: "/_matrix/client/r0/user/:user_id/account_data/:event_type", + stable_path: "/_matrix/client/v3/user/:user_id/account_data/:event_type", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// User ID of user for whom to retrieve data. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// Type of data to retrieve. + #[ruma_api(path)] + pub event_type: &'a str, + } + + response: { + /// Account data content for the given type. + /// + /// Use `ruma_events::RawExt` for deserialization. + #[ruma_api(body)] + pub account_data: Raw, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID and event type. + pub fn new(user_id: &'a UserId, event_type: &'a str) -> Self { + Self { user_id, event_type } + } + } + + impl Response { + /// Creates a new `Response` with the given account data. + pub fn new(account_data: Raw) -> Self { + Self { account_data } + } + } +} diff --git a/crates/ruma-client-api/src/config/get_room_account_data.rs b/crates/ruma-client-api/src/config/get_room_account_data.rs new file mode 100644 index 00000000..451b156e --- /dev/null +++ b/crates/ruma-client-api/src/config/get_room_account_data.rs @@ -0,0 +1,63 @@ +//! `GET /_matrix/client/*/user/{userId}/rooms/{roomId}/account_data/{type}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3useruseridroomsroomidaccount_datatype + + use ruma_api::ruma_api; + use ruma_events::AnyRoomAccountDataEventContent; + use ruma_identifiers::{RoomId, UserId}; + use ruma_serde::Raw; + + ruma_api! { + metadata: { + description: "Gets account data room for a user for a given room", + name: "get_room_account_data", + method: GET, + r0_path: "/_matrix/client/r0/user/:user_id/rooms/:room_id/account_data/:event_type", + stable_path: "/_matrix/client/v3/user/:user_id/rooms/:room_id/account_data/:event_type", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// User ID of user for whom to retrieve data. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// Room ID for which to retrieve data. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// Type of data to retrieve. + #[ruma_api(path)] + pub event_type: &'a str, + } + + response: { + /// Account data content for the given type. + /// + /// Use `ruma_events::RawExt` for deserialization. + #[ruma_api(body)] + pub account_data: Raw, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID, room ID and event type. + pub fn new(user_id: &'a UserId, room_id: &'a RoomId, event_type: &'a str) -> Self { + Self { user_id, room_id, event_type } + } + } + + impl Response { + /// Creates a new `Response` with the given account data. + pub fn new(account_data: Raw) -> Self { + Self { account_data } + } + } +} diff --git a/crates/ruma-client-api/src/config/set_global_account_data.rs b/crates/ruma-client-api/src/config/set_global_account_data.rs new file mode 100644 index 00000000..20a008d3 --- /dev/null +++ b/crates/ruma-client-api/src/config/set_global_account_data.rs @@ -0,0 +1,63 @@ +//! `PUT /_matrix/client/*/user/{userId}/account_data/{type}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3useruseridaccount_datatype + + use ruma_api::ruma_api; + use ruma_identifiers::UserId; + use serde_json::value::RawValue as RawJsonValue; + + ruma_api! { + metadata: { + description: "Sets global account data.", + method: PUT, + name: "set_global_account_data", + r0_path: "/_matrix/client/r0/user/:user_id/account_data/:event_type", + stable_path: "/_matrix/client/v3/user/:user_id/account_data/:event_type", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The ID of the user to set account_data for. + /// + /// The access token must be authorized to make requests for this user ID. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// The event type of the account_data to set. + /// + /// Custom types should be namespaced to avoid clashes. + #[ruma_api(path)] + pub event_type: &'a str, + + /// Arbitrary JSON to store as config data. + /// + /// To create a `RawJsonValue`, use `serde_json::value::to_raw_value`. + #[ruma_api(body)] + pub data: &'a RawJsonValue, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given data, event type and user ID. + pub fn new(data: &'a RawJsonValue, event_type: &'a str, user_id: &'a UserId) -> Self { + Self { user_id, event_type, data } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/config/set_room_account_data.rs b/crates/ruma-client-api/src/config/set_room_account_data.rs new file mode 100644 index 00000000..3fa1de2e --- /dev/null +++ b/crates/ruma-client-api/src/config/set_room_account_data.rs @@ -0,0 +1,72 @@ +//! `PUT /_matrix/client/*/user/{userId}/rooms/{roomId}/account_data/{type}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3useruseridroomsroomidaccount_datatype + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomId, UserId}; + use serde_json::value::RawValue as RawJsonValue; + + ruma_api! { + metadata: { + description: "Associate account data with a room.", + method: PUT, + name: "set_room_account_data", + r0_path: "/_matrix/client/r0/user/:user_id/rooms/:room_id/account_data/:event_type", + stable_path: "/_matrix/client/v3/user/:user_id/rooms/:room_id/account_data/:event_type", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// Arbitrary JSON to store as config data. + /// + /// To create a `RawJsonValue`, use `serde_json::value::to_raw_value`. + #[ruma_api(body)] + pub data: &'a RawJsonValue, + + /// The event type of the account_data to set. + /// + /// Custom types should be namespaced to avoid clashes. + #[ruma_api(path)] + pub event_type: &'a str, + + /// The ID of the room to set account_data on. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The ID of the user to set account_data for. + /// + /// The access token must be authorized to make requests for this user ID. + #[ruma_api(path)] + pub user_id: &'a UserId, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given data, event type, room ID and user ID. + pub fn new( + data: &'a RawJsonValue, + event_type: &'a str, + room_id: &'a RoomId, + user_id: &'a UserId, + ) -> Self { + Self { data, event_type, room_id, user_id } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0/context.rs b/crates/ruma-client-api/src/context.rs similarity index 100% rename from crates/ruma-client-api/src/r0/context.rs rename to crates/ruma-client-api/src/context.rs diff --git a/crates/ruma-client-api/src/context/get_context.rs b/crates/ruma-client-api/src/context/get_context.rs new file mode 100644 index 00000000..163504e9 --- /dev/null +++ b/crates/ruma-client-api/src/context/get_context.rs @@ -0,0 +1,108 @@ +//! `GET /_matrix/client/*/rooms/{roomId}/context/{eventId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidcontexteventid + + use js_int::{uint, UInt}; + use ruma_api::ruma_api; + use ruma_events::{AnyRoomEvent, AnyStateEvent}; + use ruma_identifiers::{EventId, RoomId}; + use ruma_serde::Raw; + + use crate::filter::{IncomingRoomEventFilter, RoomEventFilter}; + + ruma_api! { + metadata: { + description: "Get the events immediately preceding and following a given event.", + method: GET, + r0_path: "/_matrix/client/r0/rooms/:room_id/context/:event_id", + stable_path: "/_matrix/client/v3/rooms/:room_id/context/:event_id", + name: "get_context", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to get events from. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The event to get context around. + #[ruma_api(path)] + pub event_id: &'a EventId, + + /// The maximum number of events to return. + /// + /// Defaults to 10. + #[ruma_api(query)] + #[serde(default = "default_limit", skip_serializing_if = "is_default_limit")] + pub limit: UInt, + + /// A RoomEventFilter to filter returned events with. + #[ruma_api(query)] + #[serde( + with = "ruma_serde::json_string", + default, + skip_serializing_if = "RoomEventFilter::is_empty" + )] + pub filter: RoomEventFilter<'a>, + } + + #[derive(Default)] + response: { + /// A token that can be used to paginate backwards with. + #[serde(skip_serializing_if = "Option::is_none")] + pub start: Option, + + /// A token that can be used to paginate forwards with. + #[serde(skip_serializing_if = "Option::is_none")] + pub end: Option, + + /// A list of room events that happened just before the requested event, + /// in reverse-chronological order. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events_before: Vec>, + + /// Details of the requested event. + #[serde(skip_serializing_if = "Option::is_none")] + pub event: Option>, + + /// A list of room events that happened just after the requested event, + /// in chronological order. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events_after: Vec>, + + /// The state of the room at the last event returned. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub state: Vec>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room id and event id. + pub fn new(room_id: &'a RoomId, event_id: &'a EventId) -> Self { + Self { room_id, event_id, limit: default_limit(), filter: RoomEventFilter::default() } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Default::default() + } + } + + fn default_limit() -> UInt { + uint!(10) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_default_limit(val: &UInt) -> bool { + *val == default_limit() + } +} diff --git a/crates/ruma-client-api/src/r0/device.rs b/crates/ruma-client-api/src/device.rs similarity index 100% rename from crates/ruma-client-api/src/r0/device.rs rename to crates/ruma-client-api/src/device.rs diff --git a/crates/ruma-client-api/src/device/delete_device.rs b/crates/ruma-client-api/src/device/delete_device.rs new file mode 100644 index 00000000..b1554749 --- /dev/null +++ b/crates/ruma-client-api/src/device/delete_device.rs @@ -0,0 +1,54 @@ +//! `DELETE /_matrix/client/*/devices/{deviceId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#delete_matrixclientv3devicesdeviceid + + use ruma_api::ruma_api; + use ruma_identifiers::DeviceId; + + use crate::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; + + ruma_api! { + metadata: { + description: "Delete a device for authenticated user.", + method: DELETE, + name: "delete_device", + r0_path: "/_matrix/client/r0/devices/:device_id", + stable_path: "/_matrix/client/v3/devices/:device_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The device to delete. + #[ruma_api(path)] + pub device_id: &'a DeviceId, + + /// Additional authentication information for the user-interactive authentication API. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth: Option>, + } + + #[derive(Default)] + response: {} + + error: UiaaResponse + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given device ID. + pub fn new(device_id: &'a DeviceId) -> Self { + Self { device_id, auth: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/device/delete_devices.rs b/crates/ruma-client-api/src/device/delete_devices.rs new file mode 100644 index 00000000..a03bbf08 --- /dev/null +++ b/crates/ruma-client-api/src/device/delete_devices.rs @@ -0,0 +1,53 @@ +//! `POST /_matrix/client/*/delete_devices` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3delete_devices + + use ruma_api::ruma_api; + use ruma_identifiers::DeviceId; + + use crate::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; + + ruma_api! { + metadata: { + description: "Delete specified devices.", + method: POST, + r0_path: "/_matrix/client/r0/delete_devices", + stable_path: "/_matrix/client/v3/delete_devices", + name: "delete_devices", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// List of devices to delete. + pub devices: &'a [Box], + + /// Additional authentication information for the user-interactive authentication API. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth: Option>, + } + + #[derive(Default)] + response: {} + + error: UiaaResponse + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given device list. + pub fn new(devices: &'a [Box]) -> Self { + Self { devices, auth: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/device/get_device.rs b/crates/ruma-client-api/src/device/get_device.rs new file mode 100644 index 00000000..256cfa7c --- /dev/null +++ b/crates/ruma-client-api/src/device/get_device.rs @@ -0,0 +1,53 @@ +//! `GET /_matrix/client/*/devices/{deviceId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3devicesdeviceid + + use ruma_api::ruma_api; + use ruma_identifiers::DeviceId; + + use crate::device::Device; + + ruma_api! { + metadata: { + description: "Get a device for authenticated user.", + method: GET, + name: "get_device", + r0_path: "/_matrix/client/r0/devices/:device_id", + stable_path: "/_matrix/client/v3/devices/:device_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The device to retrieve. + #[ruma_api(path)] + pub device_id: &'a DeviceId, + } + + response: { + /// Information about the device. + #[ruma_api(body)] + pub device: Device, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given device ID. + pub fn new(device_id: &'a DeviceId) -> Self { + Self { device_id } + } + } + + impl Response { + /// Creates a new `Response` with the given device. + pub fn new(device: Device) -> Self { + Self { device } + } + } +} diff --git a/crates/ruma-client-api/src/device/get_devices.rs b/crates/ruma-client-api/src/device/get_devices.rs new file mode 100644 index 00000000..49a642ea --- /dev/null +++ b/crates/ruma-client-api/src/device/get_devices.rs @@ -0,0 +1,48 @@ +//! `GET /_matrix/client/*/devices` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3devices + + use ruma_api::ruma_api; + + use crate::device::Device; + + ruma_api! { + metadata: { + description: "Get registered devices for authenticated user.", + method: GET, + name: "get_devices", + r0_path: "/_matrix/client/r0/devices", + stable_path: "/_matrix/client/v3/devices", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// A list of all registered devices for this user + pub devices: Vec, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given devices. + pub fn new(devices: Vec) -> Self { + Self { devices } + } + } +} diff --git a/crates/ruma-client-api/src/device/update_device.rs b/crates/ruma-client-api/src/device/update_device.rs new file mode 100644 index 00000000..a476b622 --- /dev/null +++ b/crates/ruma-client-api/src/device/update_device.rs @@ -0,0 +1,54 @@ +//! `PUT /_matrix/client/*/devices/{deviceId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3devicesdeviceid + + use ruma_api::ruma_api; + use ruma_identifiers::DeviceId; + + ruma_api! { + metadata: { + description: "Update metadata for a device.", + method: PUT, + name: "update_device", + r0_path: "/_matrix/client/r0/devices/:device_id", + stable_path: "/_matrix/client/v3/devices/:device_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The device to update. + #[ruma_api(path)] + pub device_id: &'a DeviceId, + + /// The new display name for this device. + /// + /// If this is `None`, the display name won't be changed. + #[serde(skip_serializing_if = "Option::is_none")] + pub display_name: Option, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given device ID. + pub fn new(device_id: &'a DeviceId) -> Self { + Self { device_id, display_name: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0/directory.rs b/crates/ruma-client-api/src/directory.rs similarity index 100% rename from crates/ruma-client-api/src/r0/directory.rs rename to crates/ruma-client-api/src/directory.rs diff --git a/crates/ruma-client-api/src/directory/get_public_rooms.rs b/crates/ruma-client-api/src/directory/get_public_rooms.rs new file mode 100644 index 00000000..87359206 --- /dev/null +++ b/crates/ruma-client-api/src/directory/get_public_rooms.rs @@ -0,0 +1,130 @@ +//! `GET /_matrix/client/*/publicRooms` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3publicrooms + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_common::directory::PublicRoomsChunk; + use ruma_identifiers::ServerName; + + ruma_api! { + metadata: { + description: "Get the list of rooms in this homeserver's public directory.", + method: GET, + name: "get_public_rooms", + r0_path: "/_matrix/client/r0/publicRooms", + stable_path: "/_matrix/client/v3/publicRooms", + rate_limited: false, + authentication: None, + added: 1.0, + } + + #[derive(Default)] + request: { + /// Limit for the number of results to return. + #[serde(skip_serializing_if = "Option::is_none")] + #[ruma_api(query)] + pub limit: Option, + + /// Pagination token from a previous request. + #[serde(skip_serializing_if = "Option::is_none")] + #[ruma_api(query)] + pub since: Option<&'a str>, + + /// The server to fetch the public room lists from. + /// + /// `None` means the server this request is sent to. + #[serde(skip_serializing_if = "Option::is_none")] + #[ruma_api(query)] + pub server: Option<&'a ServerName>, + } + + response: { + /// A paginated chunk of public rooms. + pub chunk: Vec, + + /// A pagination token for the response. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_batch: Option, + + /// A pagination token that allows fetching previous results. + #[serde(skip_serializing_if = "Option::is_none")] + pub prev_batch: Option, + + /// An estimate on the total number of public rooms, if the server has an estimate. + #[serde(skip_serializing_if = "Option::is_none")] + pub total_room_count_estimate: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates an empty `Request`. + pub fn new() -> Self { + Default::default() + } + } + + impl Response { + /// Creates a new `Response` with the given room list chunk. + pub fn new(chunk: Vec) -> Self { + Self { chunk, next_batch: None, prev_batch: None, total_room_count_estimate: None } + } + } + + #[cfg(all(test, any(feature = "client", feature = "server")))] + mod tests { + use js_int::uint; + + #[cfg(feature = "client")] + #[test] + fn construct_request_from_refs() { + use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken}; + use ruma_identifiers::server_name; + + let req = super::Request { + limit: Some(uint!(10)), + since: Some("hello"), + server: Some(server_name!("test.tld")), + } + .try_into_http_request::>( + "https://homeserver.tld", + SendAccessToken::IfRequired("auth_tok"), + &[MatrixVersion::V1_1], + ) + .unwrap(); + + let uri = req.uri(); + let query = uri.query().unwrap(); + + assert_eq!(uri.path(), "/_matrix/client/v3/publicRooms"); + assert!(query.contains("since=hello")); + assert!(query.contains("limit=10")); + assert!(query.contains("server=test.tld")); + } + + #[cfg(feature = "server")] + #[test] + fn construct_response_from_refs() { + use ruma_api::OutgoingResponse as _; + + let res = super::Response { + chunk: vec![], + next_batch: Some("next_batch_token".into()), + prev_batch: Some("prev_batch_token".into()), + total_room_count_estimate: Some(uint!(10)), + } + .try_into_http_response::>() + .unwrap(); + + assert_eq!( + String::from_utf8_lossy(res.body()), + r#"{"chunk":[],"next_batch":"next_batch_token","prev_batch":"prev_batch_token","total_room_count_estimate":10}"# + ); + } + } +} diff --git a/crates/ruma-client-api/src/directory/get_public_rooms_filtered.rs b/crates/ruma-client-api/src/directory/get_public_rooms_filtered.rs new file mode 100644 index 00000000..abf21310 --- /dev/null +++ b/crates/ruma-client-api/src/directory/get_public_rooms_filtered.rs @@ -0,0 +1,87 @@ +//! `POST /_matrix/client/*/publicRooms` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3publicrooms + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_common::directory::{ + Filter, IncomingFilter, IncomingRoomNetwork, PublicRoomsChunk, RoomNetwork, + }; + use ruma_identifiers::ServerName; + + ruma_api! { + metadata: { + description: "Get the list of rooms in this homeserver's public directory.", + method: POST, + name: "get_public_rooms_filtered", + r0_path: "/_matrix/client/r0/publicRooms", + stable_path: "/_matrix/client/v3/publicRooms", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: { + /// The server to fetch the public room lists from. + /// + /// `None` means the server this request is sent to. + #[serde(skip_serializing_if = "Option::is_none")] + #[ruma_api(query)] + pub server: Option<&'a ServerName>, + + /// Limit for the number of results to return. + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, + + /// Pagination token from a previous request. + #[serde(skip_serializing_if = "Option::is_none")] + pub since: Option<&'a str>, + + /// Filter to apply to the results. + #[serde(default, skip_serializing_if = "Filter::is_empty")] + pub filter: Filter<'a>, + + /// Network to fetch the public room lists from. + #[serde(flatten, skip_serializing_if = "ruma_serde::is_default")] + pub room_network: RoomNetwork<'a>, + } + + #[derive(Default)] + response: { + /// A paginated chunk of public rooms. + pub chunk: Vec, + + /// A pagination token for the response. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_batch: Option, + + /// A pagination token that allows fetching previous results. + #[serde(skip_serializing_if = "Option::is_none")] + pub prev_batch: Option, + + /// An estimate on the total number of public rooms, if the server has an estimate. + #[serde(skip_serializing_if = "Option::is_none")] + pub total_room_count_estimate: Option, + } + + error: crate::Error + } + + impl Request<'_> { + /// Creates an empty `Request`. + pub fn new() -> Self { + Default::default() + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Default::default() + } + } +} diff --git a/crates/ruma-client-api/src/directory/get_room_visibility.rs b/crates/ruma-client-api/src/directory/get_room_visibility.rs new file mode 100644 index 00000000..6526d26b --- /dev/null +++ b/crates/ruma-client-api/src/directory/get_room_visibility.rs @@ -0,0 +1,52 @@ +//! `GET /_matrix/client/*/directory/list/room/{roomId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3directorylistroomroomid + + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + use crate::room::Visibility; + + ruma_api! { + metadata: { + description: "Get the visibility of a public room on a directory.", + name: "get_room_visibility", + method: GET, + r0_path: "/_matrix/client/r0/directory/list/room/:room_id", + stable_path: "/_matrix/client/v3/directory/list/room/:room_id", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// The ID of the room of which to request the visibility. + #[ruma_api(path)] + pub room_id: &'a RoomId, + } + + response: { + /// Visibility of the room. + pub visibility: Visibility, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID. + pub fn new(room_id: &'a RoomId) -> Self { + Self { room_id } + } + } + + impl Response { + /// Creates a new `Response` with the given visibility. + pub fn new(visibility: Visibility) -> Self { + Self { visibility } + } + } +} diff --git a/crates/ruma-client-api/src/directory/set_room_visibility.rs b/crates/ruma-client-api/src/directory/set_room_visibility.rs new file mode 100644 index 00000000..a806d877 --- /dev/null +++ b/crates/ruma-client-api/src/directory/set_room_visibility.rs @@ -0,0 +1,53 @@ +//! `PUT /_matrix/client/*/directory/list/room/{roomId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3directorylistroomroomid + + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + use crate::room::Visibility; + + ruma_api! { + metadata: { + description: "Set the visibility of a public room on a directory.", + name: "set_room_visibility", + method: PUT, + r0_path: "/_matrix/client/r0/directory/list/room/:room_id", + stable_path: "/_matrix/client/v3/directory/list/room/:room_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The ID of the room of which to set the visibility. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// New visibility setting for the room. + pub visibility: Visibility, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID and visibility. + pub fn new(room_id: &'a RoomId, visibility: Visibility) -> Self { + Self { room_id, visibility } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/unversioned.rs b/crates/ruma-client-api/src/discover.rs similarity index 100% rename from crates/ruma-client-api/src/unversioned.rs rename to crates/ruma-client-api/src/discover.rs diff --git a/crates/ruma-client-api/src/unversioned/discover_homeserver.rs b/crates/ruma-client-api/src/discover/discover_homeserver.rs similarity index 92% rename from crates/ruma-client-api/src/unversioned/discover_homeserver.rs rename to crates/ruma-client-api/src/discover/discover_homeserver.rs index b7c54cca..fbc7c9d7 100644 --- a/crates/ruma-client-api/src/unversioned/discover_homeserver.rs +++ b/crates/ruma-client-api/src/discover/discover_homeserver.rs @@ -1,4 +1,6 @@ -//! [GET /.well-known/matrix/client](https://matrix.org/docs/spec/client_server/r0.6.1#get-well-known-matrix-client) +//! `GET /.well-known/matrix/client` ([spec]) +//! +//! [spec]: https://spec.matrix.org/v1.2/client-server-api/#getwell-knownmatrixclient use ruma_api::ruma_api; use serde::{Deserialize, Serialize}; @@ -7,7 +9,7 @@ ruma_api! { metadata: { description: "Get discovery information about the domain.", method: GET, - name: "discover_homeserver", + name: "client_well_known", stable_path: "/.well-known/matrix/client", rate_limited: false, authentication: None, diff --git a/crates/ruma-client-api/src/unversioned/get_supported_versions.rs b/crates/ruma-client-api/src/discover/get_supported_versions.rs similarity index 96% rename from crates/ruma-client-api/src/unversioned/get_supported_versions.rs rename to crates/ruma-client-api/src/discover/get_supported_versions.rs index 424db2da..13b19de4 100644 --- a/crates/ruma-client-api/src/unversioned/get_supported_versions.rs +++ b/crates/ruma-client-api/src/discover/get_supported_versions.rs @@ -1,4 +1,6 @@ -//! [GET /_matrix/client/versions](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-versions) +//! `GET /_matrix/client/versions` ([spec]) +//! +//! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientversions use std::collections::BTreeMap; diff --git a/crates/ruma-client-api/src/r0/filter.rs b/crates/ruma-client-api/src/filter.rs similarity index 99% rename from crates/ruma-client-api/src/r0/filter.rs rename to crates/ruma-client-api/src/filter.rs index bca8c273..212e1970 100644 --- a/crates/ruma-client-api/src/r0/filter.rs +++ b/crates/ruma-client-api/src/filter.rs @@ -6,13 +6,12 @@ pub mod get_filter; mod lazy_load; mod url; -pub use lazy_load::LazyLoadOptions; -pub use url::UrlFilter; - use js_int::UInt; +pub use lazy_load::LazyLoadOptions; use ruma_identifiers::{RoomId, UserId}; use ruma_serde::{Outgoing, StringEnum}; use serde::Serialize; +pub use url::UrlFilter; use crate::PrivOwnedStr; diff --git a/crates/ruma-client-api/src/filter/create_filter.rs b/crates/ruma-client-api/src/filter/create_filter.rs new file mode 100644 index 00000000..297e665f --- /dev/null +++ b/crates/ruma-client-api/src/filter/create_filter.rs @@ -0,0 +1,103 @@ +//! `POST /_matrix/client/*/user/{userId}/filter` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3useruseridfilter + + use ruma_api::ruma_api; + use ruma_identifiers::UserId; + + use crate::filter::{FilterDefinition, IncomingFilterDefinition}; + + ruma_api! { + metadata: { + description: "Create a new filter for event retrieval.", + method: POST, + name: "create_filter", + r0_path: "/_matrix/client/r0/user/:user_id/filter", + stable_path: "/_matrix/client/v3/user/:user_id/filter", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The ID of the user uploading the filter. + /// + /// The access token must be authorized to make requests for this user ID. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// The filter definition. + #[ruma_api(body)] + pub filter: FilterDefinition<'a>, + } + + response: { + /// The ID of the filter that was created. + pub filter_id: String, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID and filter definition. + pub fn new(user_id: &'a UserId, filter: FilterDefinition<'a>) -> Self { + Self { user_id, filter } + } + } + + impl Response { + /// Creates a new `Response` with the given filter ID. + pub fn new(filter_id: String) -> Self { + Self { filter_id } + } + } + + #[cfg(all(test, any(feature = "client", feature = "server")))] + mod tests { + use matches::assert_matches; + + #[cfg(feature = "server")] + #[test] + fn deserialize_request() { + use ruma_api::IncomingRequest as _; + + use super::IncomingRequest; + + assert_matches!( + IncomingRequest::try_from_http_request( + http::Request::builder() + .method(http::Method::POST) + .uri("https://matrix.org/_matrix/client/r0/user/@foo:bar.com/filter") + .body(b"{}" as &[u8]) + .unwrap(), + &["@foo:bar.com"] + ), + Ok(IncomingRequest { user_id, filter }) + if user_id == "@foo:bar.com" && filter.is_empty() + ); + } + + #[cfg(feature = "client")] + #[test] + fn serialize_request() { + use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken}; + use ruma_identifiers::user_id; + + use crate::filter::FilterDefinition; + + assert_matches!( + super::Request::new(user_id!("@foo:bar.com"), FilterDefinition::default()) + .try_into_http_request::>( + "https://matrix.org", + SendAccessToken::IfRequired("tok"), + &[MatrixVersion::V1_1] + ), + Ok(res) if res.body() == b"{}" + ); + } + } +} diff --git a/crates/ruma-client-api/src/filter/get_filter.rs b/crates/ruma-client-api/src/filter/get_filter.rs new file mode 100644 index 00000000..ac164a11 --- /dev/null +++ b/crates/ruma-client-api/src/filter/get_filter.rs @@ -0,0 +1,89 @@ +//! `GET /_matrix/client/*/user/{userId}/filter/{filterId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3useruseridfilterfilterid + + use ruma_api::ruma_api; + use ruma_identifiers::UserId; + + use crate::filter::IncomingFilterDefinition; + + ruma_api! { + metadata: { + description: "Retrieve a previously created filter.", + method: GET, + name: "get_filter", + r0_path: "/_matrix/client/r0/user/:user_id/filter/:filter_id", + stable_path: "/_matrix/client/v3/user/:user_id/filter/:filter_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The user ID to download a filter for. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// The ID of the filter to download. + #[ruma_api(path)] + pub filter_id: &'a str, + } + + response: { + /// The filter definition. + #[ruma_api(body)] + pub filter: IncomingFilterDefinition, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID and filter ID. + pub fn new(user_id: &'a UserId, filter_id: &'a str) -> Self { + Self { user_id, filter_id } + } + } + + impl Response { + /// Creates a new `Response` with the given filter definition. + pub fn new(filter: IncomingFilterDefinition) -> Self { + Self { filter } + } + } + + #[cfg(all(test, any(feature = "client", feature = "server")))] + mod tests { + use matches::assert_matches; + + #[cfg(feature = "client")] + #[test] + fn deserialize_response() { + use ruma_api::IncomingResponse; + + assert_matches!( + super::Response::try_from_http_response( + http::Response::builder().body(b"{}" as &[u8]).unwrap(), + ), + Ok(super::Response { filter }) if filter.is_empty() + ); + } + + #[cfg(feature = "server")] + #[test] + fn serialize_response() { + use ruma_api::OutgoingResponse; + + use crate::filter::IncomingFilterDefinition; + + assert_matches!( + super::Response::new(IncomingFilterDefinition::default()) + .try_into_http_response::>(), + Ok(res) if res.body() == b"{}" + ); + } + } +} diff --git a/crates/ruma-client-api/src/r0/filter/lazy_load.rs b/crates/ruma-client-api/src/filter/lazy_load.rs similarity index 100% rename from crates/ruma-client-api/src/r0/filter/lazy_load.rs rename to crates/ruma-client-api/src/filter/lazy_load.rs diff --git a/crates/ruma-client-api/src/r0/filter/url.rs b/crates/ruma-client-api/src/filter/url.rs similarity index 100% rename from crates/ruma-client-api/src/r0/filter/url.rs rename to crates/ruma-client-api/src/filter/url.rs diff --git a/crates/ruma-client-api/src/r0/keys.rs b/crates/ruma-client-api/src/keys.rs similarity index 100% rename from crates/ruma-client-api/src/r0/keys.rs rename to crates/ruma-client-api/src/keys.rs diff --git a/crates/ruma-client-api/src/keys/claim_keys.rs b/crates/ruma-client-api/src/keys/claim_keys.rs new file mode 100644 index 00000000..180d9cb9 --- /dev/null +++ b/crates/ruma-client-api/src/keys/claim_keys.rs @@ -0,0 +1,72 @@ +//! `POST /_matrix/client/*/keys/claim` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysclaim + + use std::{collections::BTreeMap, time::Duration}; + + use ruma_api::ruma_api; + use ruma_common::encryption::OneTimeKey; + use ruma_identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UserId}; + use ruma_serde::Raw; + use serde_json::Value as JsonValue; + + ruma_api! { + metadata: { + description: "Claims one-time keys for use in pre-key messages.", + method: POST, + name: "claim_keys", + r0_path: "/_matrix/client/r0/keys/claim", + stable_path: "/_matrix/client/v3/keys/claim", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The time (in milliseconds) to wait when downloading keys from remote servers. + /// 10 seconds is the recommended default. + #[serde( + with = "ruma_serde::duration::opt_ms", + default, + skip_serializing_if = "Option::is_none", + )] + pub timeout: Option, + + /// The keys to be claimed. + pub one_time_keys: BTreeMap, BTreeMap, DeviceKeyAlgorithm>>, + } + + response: { + /// If any remote homeservers could not be reached, they are recorded here. + /// The names of the properties are the names of the unreachable servers. + pub failures: BTreeMap, + + /// One-time keys for the queried devices. + pub one_time_keys: BTreeMap, OneTimeKeys>, + } + + error: crate::Error + } + + impl Request { + /// Creates a new `Request` with the given key claims and the recommended 10 second timeout. + pub fn new( + one_time_keys: BTreeMap, BTreeMap, DeviceKeyAlgorithm>>, + ) -> Self { + Self { timeout: Some(Duration::from_secs(10)), one_time_keys } + } + } + + impl Response { + /// Creates a new `Response` with the given keys and no failures. + pub fn new(one_time_keys: BTreeMap, OneTimeKeys>) -> Self { + Self { failures: BTreeMap::new(), one_time_keys } + } + } + + /// The one-time keys for a given device. + pub type OneTimeKeys = BTreeMap, BTreeMap, Raw>>; +} diff --git a/crates/ruma-client-api/src/keys/get_key_changes.rs b/crates/ruma-client-api/src/keys/get_key_changes.rs new file mode 100644 index 00000000..7be445c1 --- /dev/null +++ b/crates/ruma-client-api/src/keys/get_key_changes.rs @@ -0,0 +1,63 @@ +//! `GET /_matrix/client/*/keys/changes` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3keyschanges + + use ruma_api::ruma_api; + use ruma_identifiers::UserId; + + ruma_api! { + metadata: { + description: "Gets a list of users who have updated their device identity keys since a previous sync token.", + method: GET, + name: "get_key_changes", + r0_path: "/_matrix/client/r0/keys/changes", + stable_path: "/_matrix/client/v3/keys/changes", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The desired start point of the list. + /// + /// Should be the next_batch field from a response to an earlier call to /sync. + #[ruma_api(query)] + pub from: &'a str, + + /// The desired end point of the list. + /// + /// Should be the next_batch field from a recent call to /sync - typically the most recent + /// such call. + #[ruma_api(query)] + pub to: &'a str, + } + + response: { + /// The Matrix User IDs of all users who updated their device identity keys. + pub changed: Vec>, + + /// The Matrix User IDs of all users who may have left all the end-to-end + /// encrypted rooms they previously shared with the user. + pub left: Vec>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given start and end points. + pub fn new(from: &'a str, to: &'a str) -> Self { + Self { from, to } + } + } + + impl Response { + /// Creates a new `Response` with the given changed and left user ID lists. + pub fn new(changed: Vec>, left: Vec>) -> Self { + Self { changed, left } + } + } +} diff --git a/crates/ruma-client-api/src/keys/get_keys.rs b/crates/ruma-client-api/src/keys/get_keys.rs new file mode 100644 index 00000000..bda7f89a --- /dev/null +++ b/crates/ruma-client-api/src/keys/get_keys.rs @@ -0,0 +1,95 @@ +//! `POST /_matrix/client/*/keys/query` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysquery + + use std::{collections::BTreeMap, time::Duration}; + + use ruma_api::ruma_api; + use ruma_common::encryption::{CrossSigningKey, DeviceKeys}; + use ruma_identifiers::{DeviceId, UserId}; + use ruma_serde::Raw; + use serde_json::Value as JsonValue; + + ruma_api! { + metadata: { + description: "Returns the current devices and identity keys for the given users.", + method: POST, + name: "get_keys", + r0_path: "/_matrix/client/r0/keys/query", + stable_path: "/_matrix/client/v3/keys/query", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: { + /// The time (in milliseconds) to wait when downloading keys from remote servers. + /// + /// 10 seconds is the recommended default. + #[serde( + with = "ruma_serde::duration::opt_ms", + default, + skip_serializing_if = "Option::is_none", + )] + pub timeout: Option, + + /// The keys to be downloaded. + /// + /// An empty list indicates all devices for the corresponding user. + pub device_keys: BTreeMap, Vec>>, + + /// If the client is fetching keys as a result of a device update received in a sync + /// request, this should be the 'since' token of that sync request, or any later sync token. + /// + /// This allows the server to ensure its response contains the keys advertised by the + /// notification in that sync. + #[serde(skip_serializing_if = "Option::is_none")] + pub token: Option<&'a str>, + } + + #[derive(Default)] + response: { + /// If any remote homeservers could not be reached, they are recorded here. + /// + /// The names of the properties are the names of the unreachable servers. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub failures: BTreeMap, + + /// Information on the queried devices. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub device_keys: BTreeMap, BTreeMap, Raw>>, + + /// Information on the master cross-signing keys of the queried users. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub master_keys: BTreeMap, Raw>, + + /// Information on the self-signing keys of the queried users. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub self_signing_keys: BTreeMap, Raw>, + + /// Information on the user-signing keys of the queried users. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub user_signing_keys: BTreeMap, Raw>, + } + + error: crate::Error + } + + impl Request<'_> { + /// Creates an empty `Request`. + pub fn new() -> Self { + Default::default() + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Default::default() + } + } +} diff --git a/crates/ruma-client-api/src/keys/upload_keys.rs b/crates/ruma-client-api/src/keys/upload_keys.rs new file mode 100644 index 00000000..0416cdde --- /dev/null +++ b/crates/ruma-client-api/src/keys/upload_keys.rs @@ -0,0 +1,67 @@ +//! `POST /_matrix/client/*/keys/upload` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysupload + + use std::collections::BTreeMap; + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_common::encryption::{DeviceKeys, OneTimeKey}; + use ruma_identifiers::{DeviceKeyAlgorithm, DeviceKeyId}; + use ruma_serde::Raw; + + ruma_api! { + metadata: { + description: "Publishes end-to-end encryption keys for the device.", + method: POST, + name: "upload_keys", + r0_path: "/_matrix/client/r0/keys/upload", + stable_path: "/_matrix/client/v3/keys/upload", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: { + /// Identity keys for the device. + /// + /// May be absent if no new identity keys are required. + #[serde(skip_serializing_if = "Option::is_none")] + pub device_keys: Option>, + + /// One-time public keys for "pre-key" messages. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub one_time_keys: BTreeMap, Raw>, + + /// Fallback public keys for "pre-key" messages. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty", rename = "org.matrix.msc2732.fallback_keys")] + pub fallback_keys: BTreeMap, Raw>, + } + + response: { + /// For each key algorithm, the number of unclaimed one-time keys of that + /// type currently held on the server for this device. + pub one_time_key_counts: BTreeMap, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Default::default() + } + } + + impl Response { + /// Creates a new `Response` with the given one time key counts. + pub fn new(one_time_key_counts: BTreeMap) -> Self { + Self { one_time_key_counts } + } + } +} diff --git a/crates/ruma-client-api/src/keys/upload_signatures.rs b/crates/ruma-client-api/src/keys/upload_signatures.rs new file mode 100644 index 00000000..9dfa4e3b --- /dev/null +++ b/crates/ruma-client-api/src/keys/upload_signatures.rs @@ -0,0 +1,107 @@ +//! `POST /_matrix/client/*/keys/signatures/upload` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keyssignaturesupload + + use std::collections::BTreeMap; + + use ruma_api::ruma_api; + use ruma_common::encryption::{CrossSigningKey, DeviceKeys}; + use ruma_identifiers::{DeviceId, UserId}; + use ruma_serde::{Raw, StringEnum}; + use serde::{Deserialize, Serialize}; + use serde_json::value::RawValue as RawJsonValue; + + use crate::PrivOwnedStr; + + ruma_api! { + metadata: { + description: "Publishes cross-signing signatures for the user.", + method: POST, + name: "upload_signatures", + unstable_path: "/_matrix/client/unstable/keys/signatures/upload", + stable_path: "/_matrix/client/v3/keys/signatures/upload", + rate_limited: false, + authentication: AccessToken, + added: 1.1, + } + + request: { + /// Signed keys. + #[ruma_api(body)] + pub signed_keys: BTreeMap, SignedKeys>, + } + + #[derive(Default)] + response: { + /// Signature processing failures. + pub failures: BTreeMap, BTreeMap>, + } + + error: crate::Error + } + + impl Request { + /// Creates a new `Request` with the given signed keys. + pub fn new(signed_keys: BTreeMap, SignedKeys>) -> Self { + Self { signed_keys } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self::default() + } + } + + /// A map of key IDs to signed key objects. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[serde(transparent)] + pub struct SignedKeys(BTreeMap, Box>); + + impl SignedKeys { + /// Creates an empty `SignedKeys` map. + pub fn new() -> Self { + Self::default() + } + + /// Add the given device keys. + pub fn add_device_keys(&mut self, device_id: Box, device_keys: Raw) { + self.0.insert(device_id.into(), device_keys.into_json()); + } + + /// Add the given cross signing keys. + pub fn add_cross_signing_keys( + &mut self, + cross_signing_key_id: Box, + cross_signing_keys: Raw, + ) { + self.0.insert(cross_signing_key_id, cross_signing_keys.into_json()); + } + } + + /// A failure to process a signed key. + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct Failure { + /// Machine-readable error code. + errcode: FailureErrorCode, + + /// Human-readable error message. + error: String, + } + + /// Error code for signed key processing failures. + #[derive(Clone, Debug, PartialEq, Eq, StringEnum)] + #[non_exhaustive] + #[ruma_enum(rename_all = "M_MATRIX_ERROR_CASE")] + pub enum FailureErrorCode { + /// The signature is invalid. + InvalidSignature, + + #[doc(hidden)] + _Custom(PrivOwnedStr), + } +} diff --git a/crates/ruma-client-api/src/keys/upload_signing_keys.rs b/crates/ruma-client-api/src/keys/upload_signing_keys.rs new file mode 100644 index 00000000..866d4109 --- /dev/null +++ b/crates/ruma-client-api/src/keys/upload_signing_keys.rs @@ -0,0 +1,70 @@ +//! `POST /_matrix/client/*/keys/device_signing/upload` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysdevice_signingupload + + use ruma_api::ruma_api; + use ruma_common::encryption::CrossSigningKey; + use ruma_serde::Raw; + + use crate::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; + + ruma_api! { + metadata: { + description: "Publishes cross signing keys for the user.", + method: POST, + name: "upload_signing_keys", + unstable_path: "/_matrix/client/unstable/keys/device_signing/upload", + stable_path: "/_matrix/client/v3/keys/device_signing/upload", + rate_limited: false, + authentication: AccessToken, + added: 1.1, + } + + #[derive(Default)] + request: { + /// Additional authentication information for the user-interactive authentication API. + #[serde(skip_serializing_if = "Option::is_none")] + pub auth: Option>, + + /// The user's master key. + #[serde(skip_serializing_if = "Option::is_none")] + pub master_key: Option>, + + /// The user's self-signing key. + /// + /// Must be signed with the accompanied master, or by the user's most recently uploaded + /// master key if no master key is included in the request. + #[serde(skip_serializing_if = "Option::is_none")] + pub self_signing_key: Option>, + + /// The user's user-signing key. + /// + /// Must be signed with the accompanied master, or by the user's most recently uploaded + /// master key if no master key is included in the request. + #[serde(skip_serializing_if = "Option::is_none")] + pub user_signing_key: Option>, + } + + #[derive(Default)] + response: {} + + error: UiaaResponse + } + + impl Request<'_> { + /// Creates an empty `Request`. + pub fn new() -> Self { + Default::default() + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0/knock.rs b/crates/ruma-client-api/src/knock.rs similarity index 100% rename from crates/ruma-client-api/src/r0/knock.rs rename to crates/ruma-client-api/src/knock.rs diff --git a/crates/ruma-client-api/src/knock/knock_room.rs b/crates/ruma-client-api/src/knock/knock_room.rs new file mode 100644 index 00000000..ed0f7c67 --- /dev/null +++ b/crates/ruma-client-api/src/knock/knock_room.rs @@ -0,0 +1,59 @@ +//! `POST /_matrix/client/*/knock/{roomIdOrAlias}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3knockroomidoralias + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomId, RoomOrAliasId, ServerName}; + + ruma_api! { + metadata: { + description: "Knock on a room.", + method: POST, + name: "knock_room", + unstable_path: "/_matrix/client/unstable/xyz.amorgan.knock/knock/:room_id_or_alias", + stable_path: "/_matrix/client/v3/knock/:room_id_or_alias", + rate_limited: true, + authentication: AccessToken, + added: 1.1, + } + + request: { + /// The room the user should knock on. + #[ruma_api(path)] + pub room_id_or_alias: &'a RoomOrAliasId, + + /// The reason for joining a room. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option<&'a str>, + + /// The servers to attempt to knock on the room through. + /// + /// One of the servers must be participating in the room. + #[ruma_api(query)] + #[serde(default, skip_serializing_if = "<[_]>::is_empty")] + pub server_name: &'a [Box], + } + + response: { + /// The room that the user knocked on. + pub room_id: Box, + } + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID or alias. + pub fn new(room_id_or_alias: &'a RoomOrAliasId) -> Self { + Self { room_id_or_alias, reason: None, server_name: &[] } + } + } + + impl Response { + /// Creates a new `Response` with the given room ID. + pub fn new(room_id: Box) -> Self { + Self { room_id } + } + } +} diff --git a/crates/ruma-client-api/src/lib.rs b/crates/ruma-client-api/src/lib.rs index f53f1d8f..29347d3c 100644 --- a/crates/ruma-client-api/src/lib.rs +++ b/crates/ruma-client-api/src/lib.rs @@ -3,15 +3,47 @@ //! (De)serializable types for the [Matrix Client-Server API][client-api]. //! These types can be shared by client and server code. //! -//! [client-api]: https://matrix.org/docs/spec/client_server/r0.6.1.html +//! [client-api]: https://spec.matrix.org/v1.2/client-server-api/ #![warn(missing_docs)] #![cfg_attr(docsrs, feature(doc_auto_cfg))] +pub mod account; +pub mod alias; +pub mod appservice; +pub mod backup; +pub mod capabilities; +pub mod config; +pub mod context; +pub mod device; +pub mod directory; +pub mod discover; pub mod error; -pub mod r0; +pub mod filter; +pub mod keys; +pub mod knock; +pub mod media; +pub mod membership; +pub mod message; +pub mod presence; +pub mod profile; +pub mod push; +pub mod read_marker; +pub mod receipt; +pub mod redact; +pub mod room; +pub mod search; +pub mod server; pub mod session; -pub mod unversioned; +pub mod state; +pub mod sync; +pub mod tag; +pub mod thirdparty; +pub mod to_device; +pub mod typing; +pub mod uiaa; +pub mod user_directory; +pub mod voip; use std::fmt; diff --git a/crates/ruma-client-api/src/r0/media.rs b/crates/ruma-client-api/src/media.rs similarity index 100% rename from crates/ruma-client-api/src/r0/media.rs rename to crates/ruma-client-api/src/media.rs diff --git a/crates/ruma-client-api/src/media/create_content.rs b/crates/ruma-client-api/src/media/create_content.rs new file mode 100644 index 00000000..33454322 --- /dev/null +++ b/crates/ruma-client-api/src/media/create_content.rs @@ -0,0 +1,86 @@ +//! `POST /_matrix/media/*/upload` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixmediav3upload + + use ruma_api::ruma_api; + use ruma_identifiers::MxcUri; + + ruma_api! { + metadata: { + description: "Upload content to the media store.", + method: POST, + name: "create_media_content", + r0_path: "/_matrix/media/r0/upload", + stable_path: "/_matrix/media/v3/upload", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The file contents to upload. + #[ruma_api(raw_body)] + pub file: &'a [u8], + + /// The name of the file being uploaded. + #[ruma_api(query)] + #[serde(skip_serializing_if = "Option::is_none")] + pub filename: Option<&'a str>, + + /// The content type of the file being uploaded. + #[ruma_api(header = CONTENT_TYPE)] + pub content_type: Option<&'a str>, + + /// Should the server return a blurhash or not. + /// + /// This uses the unstable prefix in + /// [MSC2448](https://github.com/matrix-org/matrix-doc/pull/2448). + #[ruma_api(query)] + #[cfg(feature = "unstable-msc2448")] + #[serde(default, skip_serializing_if = "ruma_serde::is_default", rename = "xyz.amorgan.blurhash")] + pub generate_blurhash: bool, + } + + response: { + /// The MXC URI for the uploaded content. + pub content_uri: Box, + + /// The [BlurHash](https://blurha.sh) for the uploaded content. + /// + /// This uses the unstable prefix in + /// [MSC2448](https://github.com/matrix-org/matrix-doc/pull/2448). + #[cfg(feature = "unstable-msc2448")] + #[serde(rename = "xyz.amorgan.blurhash", skip_serializing_if = "Option::is_none")] + pub blurhash: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given file contents. + pub fn new(file: &'a [u8]) -> Self { + Self { + file, + filename: None, + content_type: None, + #[cfg(feature = "unstable-msc2448")] + generate_blurhash: false, + } + } + } + + impl Response { + /// Creates a new `Response` with the given MXC URI. + pub fn new(content_uri: Box) -> Self { + Self { + content_uri, + #[cfg(feature = "unstable-msc2448")] + blurhash: None, + } + } + } +} diff --git a/crates/ruma-client-api/src/media/get_content.rs b/crates/ruma-client-api/src/media/get_content.rs new file mode 100644 index 00000000..34bdf7ed --- /dev/null +++ b/crates/ruma-client-api/src/media/get_content.rs @@ -0,0 +1,82 @@ +//! `GET /_matrix/media/*/download/{serverName}/{mediaId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixmediav3downloadservernamemediaid + + use ruma_api::ruma_api; + use ruma_identifiers::{Error, MxcUri, ServerName}; + + ruma_api! { + metadata: { + description: "Retrieve content from the media store.", + method: GET, + name: "get_media_content", + r0_path: "/_matrix/media/r0/download/:server_name/:media_id", + stable_path: "/_matrix/media/v3/download/:server_name/:media_id", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// The media ID from the mxc:// URI (the path component). + #[ruma_api(path)] + pub media_id: &'a str, + + /// The server name from the mxc:// URI (the authoritory component). + #[ruma_api(path)] + pub server_name: &'a ServerName, + + /// Whether to fetch media deemed remote. + /// + /// Used to prevent routing loops. Defaults to `true`. + #[ruma_api(query)] + #[serde(default = "ruma_serde::default_true", skip_serializing_if = "ruma_serde::is_true")] + pub allow_remote: bool, + } + + response: { + /// The content that was previously uploaded. + #[ruma_api(raw_body)] + pub file: Vec, + + /// The content type of the file that was previously uploaded. + #[ruma_api(header = CONTENT_TYPE)] + pub content_type: Option, + + /// The value of the `Content-Disposition` HTTP header, possibly containing the name of the + /// file that was previously uploaded. + /// + /// See [MDN] for the syntax. + /// + /// [MDN]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Syntax + #[ruma_api(header = CONTENT_DISPOSITION)] + pub content_disposition: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given media ID and server name. + pub fn new(media_id: &'a str, server_name: &'a ServerName) -> Self { + Self { media_id, server_name, allow_remote: true } + } + + /// Creates a new `Request` with the given url. + pub fn from_url(url: &'a MxcUri) -> Result { + let (server_name, media_id) = url.parts()?; + + Ok(Self { media_id, server_name, allow_remote: true }) + } + } + + impl Response { + /// Creates a new `Response` with the given file contents. + pub fn new(file: Vec) -> Self { + Self { file, content_type: None, content_disposition: None } + } + } +} diff --git a/crates/ruma-client-api/src/media/get_content_as_filename.rs b/crates/ruma-client-api/src/media/get_content_as_filename.rs new file mode 100644 index 00000000..1afd5dd4 --- /dev/null +++ b/crates/ruma-client-api/src/media/get_content_as_filename.rs @@ -0,0 +1,87 @@ +//! `GET /_matrix/media/*/download/{serverName}/{mediaId}/{fileName}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixmediav3downloadservernamemediaidfilename + + use ruma_api::ruma_api; + use ruma_identifiers::{Error, MxcUri, ServerName}; + + ruma_api! { + metadata: { + description: "Retrieve content from the media store, specifying a filename to return.", + method: GET, + name: "get_media_content_as_filename", + r0_path: "/_matrix/media/r0/download/:server_name/:media_id/:filename", + stable_path: "/_matrix/media/v3/download/:server_name/:media_id/:filename", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// The media ID from the mxc:// URI (the path component). + #[ruma_api(path)] + pub media_id: &'a str, + + /// The server name from the mxc:// URI (the authoritory component). + #[ruma_api(path)] + pub server_name: &'a ServerName, + + /// The filename to return in the `Content-Disposition` header. + #[ruma_api(path)] + pub filename: &'a str, + + /// Whether to fetch media deemed remote. + /// + /// Used to prevent routing loops. Defaults to `true`. + #[ruma_api(query)] + #[serde(default = "ruma_serde::default_true", skip_serializing_if = "ruma_serde::is_true")] + pub allow_remote: bool, + } + + response: { + /// The content that was previously uploaded. + #[ruma_api(raw_body)] + pub file: Vec, + + /// The content type of the file that was previously uploaded. + #[ruma_api(header = CONTENT_TYPE)] + // Potentially not actually optional – https://github.com/matrix-org/matrix-doc/pull/2818 + pub content_type: Option, + + /// The value of the `Content-Disposition` HTTP header, possibly containing the name of the + /// file that was previously uploaded. + /// + /// See [MDN] for the syntax. + /// + /// [MDN]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Syntax + #[ruma_api(header = CONTENT_DISPOSITION)] + pub content_disposition: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given media ID, server name and filename. + pub fn new(media_id: &'a str, server_name: &'a ServerName, filename: &'a str) -> Self { + Self { media_id, server_name, filename, allow_remote: true } + } + + /// Creates a new `Request` with the given url and filename. + pub fn from_url(url: &'a MxcUri, filename: &'a str) -> Result { + let (server_name, media_id) = url.parts()?; + + Ok(Self { media_id, server_name, filename, allow_remote: true }) + } + } + + impl Response { + /// Creates a new `Response` with the given file. + pub fn new(file: Vec) -> Self { + Self { file, content_type: None, content_disposition: None } + } + } +} diff --git a/crates/ruma-client-api/src/media/get_content_thumbnail.rs b/crates/ruma-client-api/src/media/get_content_thumbnail.rs new file mode 100644 index 00000000..e5aed252 --- /dev/null +++ b/crates/ruma-client-api/src/media/get_content_thumbnail.rs @@ -0,0 +1,126 @@ +//! `GET /_matrix/media/*/thumbnail/{serverName}/{mediaId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixmediav3thumbnailservernamemediaid + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_identifiers::{Error, MxcUri, ServerName}; + use ruma_serde::StringEnum; + + use crate::PrivOwnedStr; + + ruma_api! { + metadata: { + description: "Get a thumbnail of content from the media store.", + method: GET, + name: "get_content_thumbnail", + r0_path: "/_matrix/media/r0/thumbnail/:server_name/:media_id", + stable_path: "/_matrix/media/v3/thumbnail/:server_name/:media_id", + rate_limited: true, + authentication: None, + added: 1.0, + } + + request: { + /// The media ID from the mxc:// URI (the path component). + #[ruma_api(path)] + pub media_id: &'a str, + + /// The server name from the mxc:// URI (the authoritory component). + #[ruma_api(path)] + pub server_name: &'a ServerName, + + /// The desired resizing method. + #[ruma_api(query)] + #[serde(skip_serializing_if = "Option::is_none")] + pub method: Option, + + /// The *desired* width of the thumbnail. + /// + /// The actual thumbnail may not match the size specified. + #[ruma_api(query)] + pub width: UInt, + + /// The *desired* height of the thumbnail. + /// + /// The actual thumbnail may not match the size specified. + #[ruma_api(query)] + pub height: UInt, + + /// Whether to fetch media deemed remote. + /// + /// Used to prevent routing loops. Defaults to `true`. + #[ruma_api(query)] + #[serde(default = "ruma_serde::default_true", skip_serializing_if = "ruma_serde::is_true")] + pub allow_remote: bool, + } + + response: { + /// A thumbnail of the requested content. + #[ruma_api(raw_body)] + pub file: Vec, + + /// The content type of the thumbnail. + #[ruma_api(header = CONTENT_TYPE)] + pub content_type: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given media ID, server name, desired thumbnail width + /// and desired thumbnail height. + pub fn new( + media_id: &'a str, + server_name: &'a ServerName, + width: UInt, + height: UInt, + ) -> Self { + Self { media_id, server_name, method: None, width, height, allow_remote: true } + } + + /// Creates a new `Request` with the given url, desired thumbnail width and + /// desired thumbnail height. + pub fn from_url(url: &'a MxcUri, width: UInt, height: UInt) -> Result { + let (server_name, media_id) = url.parts()?; + + Ok(Self { media_id, server_name, method: None, width, height, allow_remote: true }) + } + } + + impl Response { + /// Creates a new `Response` with the given thumbnail. + pub fn new(file: Vec) -> Self { + Self { file, content_type: None } + } + } + + /// The desired resizing method. + /// + /// This type can hold an arbitrary string. To check for formats that are not available as a + /// documented variant here, use its string representation, obtained through `.as_str()`. + #[derive(Clone, Debug, StringEnum)] + #[ruma_enum(rename_all = "snake_case")] + #[non_exhaustive] + pub enum Method { + /// Crop the original to produce the requested image dimensions. + Crop, + + /// Maintain the original aspect ratio of the source image. + Scale, + + #[doc(hidden)] + _Custom(PrivOwnedStr), + } + + impl Method { + /// Creates a string slice from this `Method`. + pub fn as_str(&self) -> &str { + self.as_ref() + } + } +} diff --git a/crates/ruma-client-api/src/media/get_media_config.rs b/crates/ruma-client-api/src/media/get_media_config.rs new file mode 100644 index 00000000..a2a6aa68 --- /dev/null +++ b/crates/ruma-client-api/src/media/get_media_config.rs @@ -0,0 +1,48 @@ +//! `GET /_matrix/media/*/config` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixmediav3config + + use js_int::UInt; + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "Gets the config for the media repository.", + method: GET, + r0_path: "/_matrix/media/r0/config", + stable_path: "/_matrix/media/v3/config", + name: "get_media_config", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// Maximum size of upload in bytes. + #[serde(rename = "m.upload.size")] + pub upload_size: UInt, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given maximum upload size. + pub fn new(upload_size: UInt) -> Self { + Self { upload_size } + } + } +} diff --git a/crates/ruma-client-api/src/media/get_media_preview.rs b/crates/ruma-client-api/src/media/get_media_preview.rs new file mode 100644 index 00000000..2f04527f --- /dev/null +++ b/crates/ruma-client-api/src/media/get_media_preview.rs @@ -0,0 +1,101 @@ +//! `GET /_matrix/media/*/preview_url` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixmediav3preview_url + + use ruma_api::ruma_api; + use ruma_common::MilliSecondsSinceUnixEpoch; + use serde::Serialize; + use serde_json::value::{to_raw_value as to_raw_json_value, RawValue as RawJsonValue}; + + ruma_api! { + metadata: { + description: "Get a preview for a URL.", + name: "get_media_preview", + method: GET, + r0_path: "/_matrix/media/r0/preview_url", + stable_path: "/_matrix/media/v3/preview_url", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// URL to get a preview of. + #[ruma_api(query)] + pub url: &'a str, + + /// Preferred point in time (in milliseconds) to return a preview for. + #[ruma_api(query)] + pub ts: MilliSecondsSinceUnixEpoch, + } + + #[derive(Default)] + response: { + /// OpenGraph-like data for the URL. + /// + /// Differences from OpenGraph: the image size in bytes is added to the `matrix:image:size` + /// field, and `og:image` returns the MXC URI to the image, if any. + #[ruma_api(body)] + pub data: Option>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given url and timestamp. + pub fn new(url: &'a str, ts: MilliSecondsSinceUnixEpoch) -> Self { + Self { url, ts } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self { data: None } + } + + /// Creates a new `Response` with the given OpenGraph data (in a + /// `serde_json::value::RawValue`). + pub fn from_raw_value(data: Box) -> Self { + Self { data: Some(data) } + } + + /// Creates a new `Response` with the given OpenGraph data (in any kind of serializable + /// object). + pub fn from_serialize(data: &T) -> serde_json::Result { + Ok(Self { data: Some(to_raw_json_value(data)?) }) + } + } + + #[cfg(test)] + mod tests { + use serde_json::{ + from_value as from_json_value, json, + value::{to_raw_value as to_raw_json_value, RawValue as RawJsonValue}, + }; + + // Since BTreeMap> deserialization doesn't seem to + // work, test that Option works + #[test] + fn raw_json_deserialize() { + type OptRawJson = Option>; + + assert!(from_json_value::(json!(null)).unwrap().is_none()); + assert!(from_json_value::(json!("test")).unwrap().is_some()); + assert!(from_json_value::(json!({ "a": "b" })).unwrap().is_some()); + } + + // For completeness sake, make sure serialization works too + #[test] + fn raw_json_serialize() { + assert!(to_raw_json_value(&json!(null)).is_ok()); + assert!(to_raw_json_value(&json!("string")).is_ok()); + assert!(to_raw_json_value(&json!({})).is_ok()); + assert!(to_raw_json_value(&json!({ "a": "b" })).is_ok()); + } + } +} diff --git a/crates/ruma-client-api/src/r0/membership.rs b/crates/ruma-client-api/src/membership.rs similarity index 100% rename from crates/ruma-client-api/src/r0/membership.rs rename to crates/ruma-client-api/src/membership.rs diff --git a/crates/ruma-client-api/src/membership/ban_user.rs b/crates/ruma-client-api/src/membership/ban_user.rs new file mode 100644 index 00000000..f751bda1 --- /dev/null +++ b/crates/ruma-client-api/src/membership/ban_user.rs @@ -0,0 +1,55 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/ban` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidban + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomId, UserId}; + + ruma_api! { + metadata: { + description: "Ban a user from a room.", + method: POST, + name: "ban_user", + r0_path: "/_matrix/client/r0/rooms/:room_id/ban", + stable_path: "/_matrix/client/v3/rooms/:room_id/ban", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to kick the user from. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The user to ban. + pub user_id: &'a UserId, + + /// The reason for banning the user. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option<&'a str>, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room id and room id. + pub fn new(room_id: &'a RoomId, user_id: &'a UserId) -> Self { + Self { room_id, user_id, reason: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/membership/forget_room.rs b/crates/ruma-client-api/src/membership/forget_room.rs new file mode 100644 index 00000000..db88e137 --- /dev/null +++ b/crates/ruma-client-api/src/membership/forget_room.rs @@ -0,0 +1,48 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/forget` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidforget + + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + ruma_api! { + metadata: { + description: "Forget a room.", + method: POST, + name: "forget_room", + r0_path: "/_matrix/client/r0/rooms/:room_id/forget", + stable_path: "/_matrix/client/v3/rooms/:room_id/forget", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to forget. + #[ruma_api(path)] + pub room_id: &'a RoomId, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room id. + pub fn new(room_id: &'a RoomId) -> Self { + Self { room_id } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/membership/get_member_events.rs b/crates/ruma-client-api/src/membership/get_member_events.rs new file mode 100644 index 00000000..cc1ef99e --- /dev/null +++ b/crates/ruma-client-api/src/membership/get_member_events.rs @@ -0,0 +1,146 @@ +//! `GET /_matrix/client/*/rooms/{roomId}/members` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidmembers + + use ruma_api::ruma_api; + use ruma_events::room::member::RoomMemberEvent; + use ruma_identifiers::RoomId; + use ruma_serde::{Raw, StringEnum}; + + use crate::PrivOwnedStr; + + ruma_api! { + metadata: { + description: "Get membership events for a room.", + method: GET, + name: "get_member_events", + r0_path: "/_matrix/client/r0/rooms/:room_id/members", + stable_path: "/_matrix/client/v3/rooms/:room_id/members", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to get the member events for. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The point in time (pagination token) to return members for in the room. + /// + /// This token can be obtained from a prev_batch token returned for each room by the sync + /// API. + #[serde(skip_serializing_if = "Option::is_none")] + #[ruma_api(query)] + pub at: Option<&'a str>, + + /// The kind of memberships to filter for. + /// + /// Defaults to no filtering if unspecified. When specified alongside not_membership, the + /// two parameters create an 'or' condition: either the membership is the same as membership + /// or is not the same as not_membership. + #[serde(skip_serializing_if = "Option::is_none")] + #[ruma_api(query)] + pub membership: Option, + + /// The kind of memberships to *exclude* from the results. + /// + /// Defaults to no filtering if unspecified. + #[serde(skip_serializing_if = "Option::is_none")] + #[ruma_api(query)] + pub not_membership: Option, + } + + response: { + /// A list of member events. + pub chunk: Vec>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID. + pub fn new(room_id: &'a RoomId) -> Self { + Self { room_id, at: None, membership: None, not_membership: None } + } + } + + impl Response { + /// Creates a new `Response` with the given member event chunk. + pub fn new(chunk: Vec>) -> Self { + Self { chunk } + } + } + + /// The kind of membership events to filter for. + /// + /// This type can hold an arbitrary string. To check for formats that are not available as a + /// documented variant here, use its string representation, obtained through `.as_str()`. + #[derive(Clone, Debug, PartialEq, Eq, StringEnum)] + #[ruma_enum(rename_all = "lowercase")] + #[non_exhaustive] + pub enum MembershipEventFilter { + /// The user has joined. + Join, + + /// The user has been invited. + Invite, + + /// The user has left. + Leave, + + /// The user has been banned. + Ban, + + #[doc(hidden)] + _Custom(PrivOwnedStr), + } + + impl MembershipEventFilter { + /// Creates a string slice from this `MembershipEventFilter`. + pub fn as_str(&self) -> &str { + self.as_ref() + } + } + + #[cfg(all(test, feature = "server"))] + mod tests { + use matches::assert_matches; + use ruma_api::IncomingRequest as _; + + use super::{IncomingRequest, MembershipEventFilter}; + + #[test] + fn deserialization() { + let uri = http::Uri::builder() + .scheme("https") + .authority("example.org") + .path_and_query( + "/_matrix/client/r0/rooms/!dummy%3Aexample.org/members\ + ?not_membership=leave\ + &at=1026", + ) + .build() + .unwrap(); + + let req = IncomingRequest::try_from_http_request( + http::Request::builder().uri(uri).body(&[] as &[u8]).unwrap(), + &["!dummy:example.org"], + ); + + assert_matches!( + req, + Ok(IncomingRequest { + room_id, + at: Some(at), + membership: None, + not_membership: Some(MembershipEventFilter::Leave), + }) if room_id == "!dummy:example.org" && at == "1026" + ); + } + } +} diff --git a/crates/ruma-client-api/src/membership/invite_user.rs b/crates/ruma-client-api/src/membership/invite_user.rs new file mode 100644 index 00000000..e0d6dacd --- /dev/null +++ b/crates/ruma-client-api/src/membership/invite_user.rs @@ -0,0 +1,120 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/invite` + +pub mod v3 { + //! `/v3/` ([spec (MXID)][spec-mxid], [spec (3PID)][spec-3pid]) + //! + //! This endpoint has two forms: one to invite a user + //! [by their Matrix identifier][spec-mxid], and one to invite a user + //! [by their third party identifier][spec-3pid]. + //! + //! [spec-mxid]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite + //! [spec-3pid]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite-1 + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomId, UserId}; + use ruma_serde::Outgoing; + use serde::Serialize; + + use crate::membership::{IncomingInvite3pid, Invite3pid}; + + ruma_api! { + metadata: { + description: "Invite a user to a room.", + method: POST, + name: "invite_user", + r0_path: "/_matrix/client/r0/rooms/:room_id/invite", + stable_path: "/_matrix/client/v3/rooms/:room_id/invite", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room where the user should be invited. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The user to invite. + #[serde(flatten)] + pub recipient: InvitationRecipient<'a>, + + /// Optional reason for inviting the user. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option<&'a str>, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID and invitation recipient. + pub fn new(room_id: &'a RoomId, recipient: InvitationRecipient<'a>) -> Self { + Self { room_id, recipient, reason: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } + + /// Distinguishes between invititations by Matrix or third party identifiers. + #[derive(Clone, Debug, PartialEq, Outgoing, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[incoming_derive(PartialEq)] + #[serde(untagged)] + pub enum InvitationRecipient<'a> { + /// Used to invite user by their Matrix identifier. + UserId { + /// Matrix identifier of user. + user_id: &'a UserId, + }, + + /// Used to invite user by a third party identifier. + ThirdPartyId(Invite3pid<'a>), + } + + #[cfg(test)] + mod tests { + use ruma_common::thirdparty::Medium; + use ruma_identifiers::user_id; + use serde_json::{from_value as from_json_value, json}; + + use super::IncomingInvitationRecipient; + use crate::membership::IncomingInvite3pid; + + #[test] + fn deserialize_invite_by_user_id() { + let incoming = from_json_value::( + json!({ "user_id": "@carl:example.org" }), + ) + .unwrap(); + let user_id = user_id!("@carl:example.org").to_owned(); + let recipient = IncomingInvitationRecipient::UserId { user_id }; + assert_eq!(incoming, recipient); + } + + #[test] + fn deserialize_invite_by_3pid() { + let incoming = from_json_value::(json!({ + "id_server": "example.org", + "id_access_token": "abcdefghijklmnop", + "medium": "email", + "address": "carl@example.org" + })) + .unwrap(); + let recipient = IncomingInvitationRecipient::ThirdPartyId(IncomingInvite3pid { + id_server: "example.org".into(), + id_access_token: "abcdefghijklmnop".into(), + medium: Medium::Email, + address: "carl@example.org".into(), + }); + assert_eq!(incoming, recipient); + } + } +} diff --git a/crates/ruma-client-api/src/membership/join_room_by_id.rs b/crates/ruma-client-api/src/membership/join_room_by_id.rs new file mode 100644 index 00000000..6a0b7fec --- /dev/null +++ b/crates/ruma-client-api/src/membership/join_room_by_id.rs @@ -0,0 +1,61 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/join` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidjoin + + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + use crate::membership::{IncomingThirdPartySigned, ThirdPartySigned}; + + ruma_api! { + metadata: { + description: "Join a room using its ID.", + method: POST, + name: "join_room_by_id", + r0_path: "/_matrix/client/r0/rooms/:room_id/join", + stable_path: "/_matrix/client/v3/rooms/:room_id/join", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room where the user should be invited. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The signature of a `m.third_party_invite` token to prove that this user owns a third + /// party identity which has been invited to the room. + #[serde(skip_serializing_if = "Option::is_none")] + pub third_party_signed: Option>, + + /// Optional reason for joining the room. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option<&'a str>, + } + + response: { + /// The room that the user joined. + pub room_id: Box, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room id. + pub fn new(room_id: &'a RoomId) -> Self { + Self { room_id, third_party_signed: None, reason: None } + } + } + + impl Response { + /// Creates a new `Response` with the given room id. + pub fn new(room_id: Box) -> Self { + Self { room_id } + } + } +} diff --git a/crates/ruma-client-api/src/membership/join_room_by_id_or_alias.rs b/crates/ruma-client-api/src/membership/join_room_by_id_or_alias.rs new file mode 100644 index 00000000..810d86ac --- /dev/null +++ b/crates/ruma-client-api/src/membership/join_room_by_id_or_alias.rs @@ -0,0 +1,68 @@ +//! `POST /_matrix/client/*/join/{roomIdOrAlias}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3joinroomidoralias + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomId, RoomOrAliasId, ServerName}; + + use crate::membership::{IncomingThirdPartySigned, ThirdPartySigned}; + + ruma_api! { + metadata: { + description: "Join a room using its ID or one of its aliases.", + method: POST, + name: "join_room_by_id_or_alias", + r0_path: "/_matrix/client/r0/join/:room_id_or_alias", + stable_path: "/_matrix/client/v3/join/:room_id_or_alias", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room where the user should be invited. + #[ruma_api(path)] + pub room_id_or_alias: &'a RoomOrAliasId, + + /// The servers to attempt to join the room through. + /// + /// One of the servers must be participating in the room. + #[ruma_api(query)] + #[serde(default, skip_serializing_if = "<[_]>::is_empty")] + pub server_name: &'a [Box], + + /// The signature of a `m.third_party_invite` token to prove that this user owns a third + /// party identity which has been invited to the room. + #[serde(skip_serializing_if = "Option::is_none")] + pub third_party_signed: Option>, + + /// Optional reason for joining the room. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option<&'a str>, + } + + response: { + /// The room that the user joined. + pub room_id: Box, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID or alias ID. + pub fn new(room_id_or_alias: &'a RoomOrAliasId) -> Self { + Self { room_id_or_alias, server_name: &[], third_party_signed: None, reason: None } + } + } + + impl Response { + /// Creates a new `Response` with the given room ID. + pub fn new(room_id: Box) -> Self { + Self { room_id } + } + } +} diff --git a/crates/ruma-client-api/src/membership/joined_members.rs b/crates/ruma-client-api/src/membership/joined_members.rs new file mode 100644 index 00000000..365d199a --- /dev/null +++ b/crates/ruma-client-api/src/membership/joined_members.rs @@ -0,0 +1,116 @@ +//! `GET /_matrix/client/*/rooms/{roomId}/joined_members` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidjoined_members + + use std::collections::BTreeMap; + + use ruma_api::ruma_api; + use ruma_identifiers::{MxcUri, RoomId, UserId}; + use serde::{Deserialize, Serialize}; + + ruma_api! { + metadata: { + description: "Get a map of user ids to member info objects for members of the room. Primarily for use in Application Services.", + method: GET, + name: "joined_members", + r0_path: "/_matrix/client/r0/rooms/:room_id/joined_members", + stable_path: "/_matrix/client/v3/rooms/:room_id/joined_members", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to get the members of. + #[ruma_api(path)] + pub room_id: &'a RoomId, + } + + response: { + /// A list of the rooms the user is in, i.e. + /// the ID of each room in which the user has joined membership. + pub joined: BTreeMap, RoomMember>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID. + pub fn new(room_id: &'a RoomId) -> Self { + Self { room_id } + } + } + + impl Response { + /// Creates a new `Response` with the given joined rooms. + pub fn new(joined: BTreeMap, RoomMember>) -> Self { + Self { joined } + } + } + + /// Information about a room member. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct RoomMember { + /// The display name of the user. + #[serde(skip_serializing_if = "Option::is_none")] + pub display_name: Option, + + /// The mxc avatar url of the user. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will + /// result in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub avatar_url: Option>, + } + + impl RoomMember { + /// Creates an empty `RoomMember`. + pub fn new() -> Self { + Default::default() + } + } + + #[cfg(test)] + mod test { + use matches::assert_matches; + use serde_json::{from_value as from_json_value, json}; + + use super::RoomMember; + + #[test] + fn deserialize_room_member() { + assert_matches!( + from_json_value::(json!({ + "display_name": "alice", + "avatar_url": "mxc://localhost/wefuiwegh8742w", + })).unwrap(), + RoomMember { + display_name: Some(display_name), + avatar_url: Some(avatar_url), + } if display_name == "alice" + && avatar_url == "mxc://localhost/wefuiwegh8742w" + ); + + #[cfg(feature = "compat")] + assert_matches!( + from_json_value::(json!({ + "display_name": "alice", + "avatar_url": "", + })).unwrap(), + RoomMember { + display_name: Some(display_name), + avatar_url: None, + } if display_name == "alice" + ); + } + } +} diff --git a/crates/ruma-client-api/src/membership/joined_rooms.rs b/crates/ruma-client-api/src/membership/joined_rooms.rs new file mode 100644 index 00000000..bc0556c1 --- /dev/null +++ b/crates/ruma-client-api/src/membership/joined_rooms.rs @@ -0,0 +1,48 @@ +//! `GET /_matrix/client/*/joined_rooms` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3joined_rooms + + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + ruma_api! { + metadata: { + description: "Get a list of the user's current rooms.", + method: GET, + name: "joined_rooms", + r0_path: "/_matrix/client/r0/joined_rooms", + stable_path: "/_matrix/client/v3/joined_rooms", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// A list of the rooms the user is in, i.e. the ID of each room in + /// which the user has joined membership. + pub joined_rooms: Vec>, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given joined rooms. + pub fn new(joined_rooms: Vec>) -> Self { + Self { joined_rooms } + } + } +} diff --git a/crates/ruma-client-api/src/membership/kick_user.rs b/crates/ruma-client-api/src/membership/kick_user.rs new file mode 100644 index 00000000..915a7050 --- /dev/null +++ b/crates/ruma-client-api/src/membership/kick_user.rs @@ -0,0 +1,55 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/kick` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidkick + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomId, UserId}; + + ruma_api! { + metadata: { + description: "Kick a user from a room.", + method: POST, + name: "kick_user", + r0_path: "/_matrix/client/r0/rooms/:room_id/kick", + stable_path: "/_matrix/client/v3/rooms/:room_id/kick", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to kick the user from. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The user to kick. + pub user_id: &'a UserId, + + /// The reason for kicking the user. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option<&'a str>, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room id and room id. + pub fn new(room_id: &'a RoomId, user_id: &'a UserId) -> Self { + Self { room_id, user_id, reason: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/membership/leave_room.rs b/crates/ruma-client-api/src/membership/leave_room.rs new file mode 100644 index 00000000..0062587e --- /dev/null +++ b/crates/ruma-client-api/src/membership/leave_room.rs @@ -0,0 +1,52 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/leave` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidleave + + use ruma_api::ruma_api; + use ruma_identifiers::RoomId; + + ruma_api! { + metadata: { + description: "Leave a room.", + method: POST, + name: "leave_room", + r0_path: "/_matrix/client/r0/rooms/:room_id/leave", + stable_path: "/_matrix/client/v3/rooms/:room_id/leave", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to leave. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// Optional reason to be included as the `reason` on the subsequent membership event. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option<&'a str>, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room id. + pub fn new(room_id: &'a RoomId) -> Self { + Self { room_id, reason: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/membership/unban_user.rs b/crates/ruma-client-api/src/membership/unban_user.rs new file mode 100644 index 00000000..32a57fa9 --- /dev/null +++ b/crates/ruma-client-api/src/membership/unban_user.rs @@ -0,0 +1,55 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/unban` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidunban + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomId, UserId}; + + ruma_api! { + metadata: { + description: "Unban a user from a room.", + method: POST, + name: "unban_user", + r0_path: "/_matrix/client/r0/rooms/:room_id/unban", + stable_path: "/_matrix/client/v3/rooms/:room_id/unban", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to unban the user from. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The user to unban. + pub user_id: &'a UserId, + + /// Optional reason for unbanning the user. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option<&'a str>, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room id and room id. + pub fn new(room_id: &'a RoomId, user_id: &'a UserId) -> Self { + Self { room_id, user_id, reason: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0/message.rs b/crates/ruma-client-api/src/message.rs similarity index 100% rename from crates/ruma-client-api/src/r0/message.rs rename to crates/ruma-client-api/src/message.rs diff --git a/crates/ruma-client-api/src/message/get_message_events.rs b/crates/ruma-client-api/src/message/get_message_events.rs new file mode 100644 index 00000000..395e271b --- /dev/null +++ b/crates/ruma-client-api/src/message/get_message_events.rs @@ -0,0 +1,222 @@ +//! `GET /_matrix/client/*/rooms/{roomId}/messages` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidmessages + + use js_int::{uint, UInt}; + use ruma_api::ruma_api; + use ruma_events::{AnyRoomEvent, AnyStateEvent}; + use ruma_identifiers::RoomId; + use ruma_serde::Raw; + use serde::{Deserialize, Serialize}; + + use crate::filter::{IncomingRoomEventFilter, RoomEventFilter}; + + ruma_api! { + metadata: { + description: "Get message events for a room.", + method: GET, + name: "get_message_events", + r0_path: "/_matrix/client/r0/rooms/:room_id/messages", + stable_path: "/_matrix/client/v3/rooms/:room_id/messages", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to get events from. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The token to start returning events from. + /// + /// This token can be obtained from a + /// prev_batch token returned for each room by the sync API, or from a start or end token + /// returned by a previous request to this endpoint. + #[ruma_api(query)] + pub from: &'a str, + + /// The token to stop returning events at. + /// + /// This token can be obtained from a prev_batch + /// token returned for each room by the sync endpoint, or from a start or end token returned + /// by a previous request to this endpoint. + #[serde(skip_serializing_if = "Option::is_none")] + #[ruma_api(query)] + pub to: Option<&'a str>, + + /// The direction to return events from. + #[ruma_api(query)] + pub dir: Direction, + + /// The maximum number of events to return. + /// + /// Default: 10. + #[ruma_api(query)] + #[serde(default = "default_limit", skip_serializing_if = "is_default_limit")] + pub limit: UInt, + + /// A RoomEventFilter to filter returned events with. + #[ruma_api(query)] + #[serde( + with = "ruma_serde::json_string", + default, + skip_serializing_if = "RoomEventFilter::is_empty" + )] + pub filter: RoomEventFilter<'a>, + } + + #[derive(Default)] + response: { + /// The token the pagination starts from. + pub start: String, + + /// The token the pagination ends at. + #[serde(skip_serializing_if = "Option::is_none")] + pub end: Option, + + /// A list of room events. + #[serde(default)] + pub chunk: Vec>, + + /// A list of state events relevant to showing the `chunk`. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub state: Vec>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given parameters. + /// + /// All other parameters will be defaulted. + pub fn new(room_id: &'a RoomId, from: &'a str, dir: Direction) -> Self { + Self { + room_id, + from, + to: None, + dir, + limit: default_limit(), + filter: RoomEventFilter::default(), + } + } + + /// Creates a new `Request` with the given room ID and from token, and `dir` set to + /// `Backward`. + pub fn backward(room_id: &'a RoomId, from: &'a str) -> Self { + Self::new(room_id, from, Direction::Backward) + } + + /// Creates a new `Request` with the given room ID and from token, and `dir` set to + /// `Forward`. + pub fn forward(room_id: &'a RoomId, from: &'a str) -> Self { + Self::new(room_id, from, Direction::Forward) + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Default::default() + } + } + + fn default_limit() -> UInt { + uint!(10) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_default_limit(val: &UInt) -> bool { + *val == default_limit() + } + + /// The direction to return events from. + #[derive(Clone, Debug, Deserialize, Serialize)] + #[allow(clippy::exhaustive_enums)] + pub enum Direction { + /// Return events backwards in time from the requested `from` token. + #[serde(rename = "b")] + Backward, + + /// Return events forwards in time from the requested `from` token. + #[serde(rename = "f")] + Forward, + } + + #[cfg(all(test, feature = "client"))] + mod tests { + use js_int::uint; + use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken}; + use ruma_identifiers::room_id; + + use super::{Direction, Request}; + use crate::filter::{LazyLoadOptions, RoomEventFilter}; + + #[test] + fn serialize_some_room_event_filter() { + let room_id = room_id!("!roomid:example.org"); + let rooms = &[room_id.to_owned()]; + let filter = RoomEventFilter { + lazy_load_options: LazyLoadOptions::Enabled { include_redundant_members: true }, + rooms: Some(rooms), + not_rooms: &[ + room_id!("!room:example.org").to_owned(), + room_id!("!room2:example.org").to_owned(), + room_id!("!room3:example.org").to_owned(), + ], + not_types: &["type".into()], + ..Default::default() + }; + let req = Request { + room_id, + from: "token", + to: Some("token2"), + dir: Direction::Backward, + limit: uint!(0), + filter, + }; + + let request: http::Request> = req + .try_into_http_request( + "https://homeserver.tld", + SendAccessToken::IfRequired("auth_tok"), + &[MatrixVersion::V1_1], + ) + .unwrap(); + assert_eq!( + "from=token\ + &to=token2\ + &dir=b\ + &limit=0\ + &filter=%7B%22not_types%22%3A%5B%22type%22%5D%2C%22not_rooms%22%3A%5B%22%21room%3Aexample.org%22%2C%22%21room2%3Aexample.org%22%2C%22%21room3%3Aexample.org%22%5D%2C%22rooms%22%3A%5B%22%21roomid%3Aexample.org%22%5D%2C%22lazy_load_members%22%3Atrue%2C%22include_redundant_members%22%3Atrue%7D", + request.uri().query().unwrap() + ); + } + + #[test] + fn serialize_default_room_event_filter() { + let room_id = room_id!("!roomid:example.org"); + let req = Request { + room_id, + from: "token", + to: Some("token2"), + dir: Direction::Backward, + limit: uint!(0), + filter: RoomEventFilter::default(), + }; + + let request = req + .try_into_http_request::>( + "https://homeserver.tld", + SendAccessToken::IfRequired("auth_tok"), + &[MatrixVersion::V1_1], + ) + .unwrap(); + assert_eq!("from=token&to=token2&dir=b&limit=0", request.uri().query().unwrap(),); + } + } +} diff --git a/crates/ruma-client-api/src/message/send_message_event.rs b/crates/ruma-client-api/src/message/send_message_event.rs new file mode 100644 index 00000000..ca87a5bc --- /dev/null +++ b/crates/ruma-client-api/src/message/send_message_event.rs @@ -0,0 +1,94 @@ +//! `PUT /_matrix/client/*/rooms/{roomId}/send/{eventType}/{txnId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid + + use ruma_api::ruma_api; + use ruma_events::{AnyMessageEventContent, MessageEventContent}; + use ruma_identifiers::{EventId, RoomId, TransactionId}; + use ruma_serde::Raw; + use serde_json::value::to_raw_value as to_raw_json_value; + + ruma_api! { + metadata: { + description: "Send a message event to a room.", + method: PUT, + name: "create_message_event", + r0_path: "/_matrix/client/r0/rooms/:room_id/send/:event_type/:txn_id", + stable_path: "/_matrix/client/v3/rooms/:room_id/send/:event_type/:txn_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to send the event to. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The type of event to send. + #[ruma_api(path)] + pub event_type: &'a str, + + /// The transaction ID for this event. + /// + /// Clients should generate an ID unique across requests with the + /// same access token; it will be used by the server to ensure + /// idempotency of requests. + #[ruma_api(path)] + pub txn_id: &'a TransactionId, + + /// The event content to send. + #[ruma_api(body)] + pub body: Raw, + } + + response: { + /// A unique identifier for the event. + pub event_id: Box, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room id, transaction id and event content. + /// + /// # Errors + /// + /// Since `Request` stores the request body in serialized form, this function can fail if + /// `T`s [`Serialize`][serde::Serialize] implementation can fail. + pub fn new( + room_id: &'a RoomId, + txn_id: &'a TransactionId, + content: &'a T, + ) -> serde_json::Result { + Ok(Self { + room_id, + txn_id, + event_type: content.event_type(), + body: Raw::from_json(to_raw_json_value(content)?), + }) + } + + /// Creates a new `Request` with the given room id, transaction id, event type and raw event + /// content. + pub fn new_raw( + room_id: &'a RoomId, + txn_id: &'a TransactionId, + event_type: &'a str, + body: Raw, + ) -> Self { + Self { room_id, event_type, txn_id, body } + } + } + + impl Response { + /// Creates a new `Response` with the given event id. + pub fn new(event_id: Box) -> Self { + Self { event_id } + } + } +} diff --git a/crates/ruma-client-api/src/r0/presence.rs b/crates/ruma-client-api/src/presence.rs similarity index 100% rename from crates/ruma-client-api/src/r0/presence.rs rename to crates/ruma-client-api/src/presence.rs diff --git a/crates/ruma-client-api/src/presence/get_presence.rs b/crates/ruma-client-api/src/presence/get_presence.rs new file mode 100644 index 00000000..6825a92a --- /dev/null +++ b/crates/ruma-client-api/src/presence/get_presence.rs @@ -0,0 +1,69 @@ +//! `GET /_matrix/client/*/presence/{userId}/status` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3presenceuseridstatus + + use std::time::Duration; + + use ruma_api::ruma_api; + use ruma_common::presence::PresenceState; + use ruma_identifiers::UserId; + + ruma_api! { + metadata: { + description: "Get presence status for this user.", + method: GET, + name: "get_presence", + r0_path: "/_matrix/client/r0/presence/:user_id/status", + stable_path: "/_matrix/client/v3/presence/:user_id/status", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The user whose presence state will be retrieved. + #[ruma_api(path)] + pub user_id: &'a UserId, + } + + response: { + /// The state message for this user if one was set. + #[serde(skip_serializing_if = "Option::is_none")] + pub status_msg: Option, + + /// Whether or not the user is currently active. + #[serde(skip_serializing_if = "Option::is_none")] + pub currently_active: Option, + + /// The length of time in milliseconds since an action was performed by the user. + #[serde( + with = "ruma_serde::duration::opt_ms", + default, + skip_serializing_if = "Option::is_none", + )] + pub last_active_ago: Option, + + /// The user's presence state. + pub presence: PresenceState, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID. + pub fn new(user_id: &'a UserId) -> Self { + Self { user_id } + } + } + + impl Response { + /// Creates a new `Response` with the given presence state. + pub fn new(presence: PresenceState) -> Self { + Self { presence, status_msg: None, currently_active: None, last_active_ago: None } + } + } +} diff --git a/crates/ruma-client-api/src/presence/set_presence.rs b/crates/ruma-client-api/src/presence/set_presence.rs new file mode 100644 index 00000000..d00fa3e3 --- /dev/null +++ b/crates/ruma-client-api/src/presence/set_presence.rs @@ -0,0 +1,56 @@ +//! `PUT /_matrix/client/*/presence/{userId}/status` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3presenceuseridstatus + + use ruma_api::ruma_api; + use ruma_common::presence::PresenceState; + use ruma_identifiers::UserId; + + ruma_api! { + metadata: { + description: "Set presence status for this user.", + method: PUT, + name: "set_presence", + r0_path: "/_matrix/client/r0/presence/:user_id/status", + stable_path: "/_matrix/client/v3/presence/:user_id/status", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The user whose presence state will be updated. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// The new presence state. + pub presence: PresenceState, + + /// The status message to attach to this state. + #[serde(skip_serializing_if = "Option::is_none")] + pub status_msg: Option<&'a str>, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID and presence state. + pub fn new(user_id: &'a UserId, presence: PresenceState) -> Self { + Self { user_id, presence, status_msg: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0/profile.rs b/crates/ruma-client-api/src/profile.rs similarity index 100% rename from crates/ruma-client-api/src/r0/profile.rs rename to crates/ruma-client-api/src/profile.rs diff --git a/crates/ruma-client-api/src/profile/get_avatar_url.rs b/crates/ruma-client-api/src/profile/get_avatar_url.rs new file mode 100644 index 00000000..0c59de84 --- /dev/null +++ b/crates/ruma-client-api/src/profile/get_avatar_url.rs @@ -0,0 +1,71 @@ +//! `GET /_matrix/client/*/profile/{userId}/avatar_url` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuseridavatar_url + + use ruma_api::ruma_api; + use ruma_identifiers::{MxcUri, UserId}; + + ruma_api! { + metadata: { + description: "Get the avatar URL of a user.", + method: GET, + name: "get_avatar_url", + r0_path: "/_matrix/client/r0/profile/:user_id/avatar_url", + stable_path: "/_matrix/client/v3/profile/:user_id/avatar_url", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// The user whose avatar URL will be retrieved. + #[ruma_api(path)] + pub user_id: &'a UserId, + } + + #[derive(Default)] + response: { + /// The user's avatar URL, if set. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will result + /// in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub avatar_url: Option>, + + /// The [BlurHash](https://blurha.sh) for the avatar pointed to by `avatar_url`. + /// + /// This uses the unstable prefix in + /// [MSC2448](https://github.com/matrix-org/matrix-doc/pull/2448). + #[cfg(feature = "unstable-msc2448")] + #[serde(rename = "xyz.amorgan.blurhash", skip_serializing_if = "Option::is_none")] + pub blurhash: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID. + pub fn new(user_id: &'a UserId) -> Self { + Self { user_id } + } + } + + impl Response { + /// Creates a new `Response` with the given avatar URL. + pub fn new(avatar_url: Option>) -> Self { + Self { + avatar_url, + #[cfg(feature = "unstable-msc2448")] + blurhash: None, + } + } + } +} diff --git a/crates/ruma-client-api/src/profile/get_display_name.rs b/crates/ruma-client-api/src/profile/get_display_name.rs new file mode 100644 index 00000000..0377e3e8 --- /dev/null +++ b/crates/ruma-client-api/src/profile/get_display_name.rs @@ -0,0 +1,52 @@ +//! `GET /_matrix/client/*/profile/{userId}/displayname` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuseriddisplayname + + use ruma_api::ruma_api; + use ruma_identifiers::UserId; + + ruma_api! { + metadata: { + description: "Get the display name of a user.", + method: GET, + name: "get_display_name", + r0_path: "/_matrix/client/r0/profile/:user_id/displayname", + stable_path: "/_matrix/client/v3/profile/:user_id/displayname", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// The user whose display name will be retrieved. + #[ruma_api(path)] + pub user_id: &'a UserId, + } + + #[derive(Default)] + response: { + /// The user's display name, if set. + #[serde(skip_serializing_if = "Option::is_none")] + pub displayname: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID. + pub fn new(user_id: &'a UserId) -> Self { + Self { user_id } + } + } + + impl Response { + /// Creates a new `Response` with the given display name. + pub fn new(displayname: Option) -> Self { + Self { displayname } + } + } +} diff --git a/crates/ruma-client-api/src/profile/get_profile.rs b/crates/ruma-client-api/src/profile/get_profile.rs new file mode 100644 index 00000000..984bd5c0 --- /dev/null +++ b/crates/ruma-client-api/src/profile/get_profile.rs @@ -0,0 +1,76 @@ +//! `GET /_matrix/client/*/profile/{userId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuserid + + use ruma_api::ruma_api; + use ruma_identifiers::{MxcUri, UserId}; + + ruma_api! { + metadata: { + description: "Get all profile information of an user.", + method: GET, + name: "get_profile", + r0_path: "/_matrix/client/r0/profile/:user_id", + stable_path: "/_matrix/client/v3/profile/:user_id", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// The user whose profile will be retrieved. + #[ruma_api(path)] + pub user_id: &'a UserId, + } + + #[derive(Default)] + response: { + /// The user's avatar URL, if set. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will result + /// in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub avatar_url: Option>, + + /// The user's display name, if set. + #[serde(skip_serializing_if = "Option::is_none")] + pub displayname: Option, + + /// The [BlurHash](https://blurha.sh) for the avatar pointed to by `avatar_url`. + /// + /// This uses the unstable prefix in + /// [MSC2448](https://github.com/matrix-org/matrix-doc/pull/2448). + #[cfg(feature = "unstable-msc2448")] + #[serde(rename = "xyz.amorgan.blurhash", skip_serializing_if = "Option::is_none")] + pub blurhash: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID. + pub fn new(user_id: &'a UserId) -> Self { + Self { user_id } + } + } + + impl Response { + /// Creates a new `Response` with the given avatar URL and display name. + pub fn new(avatar_url: Option>, displayname: Option) -> Self { + Self { + avatar_url, + displayname, + #[cfg(feature = "unstable-msc2448")] + blurhash: None, + } + } + } +} diff --git a/crates/ruma-client-api/src/profile/set_avatar_url.rs b/crates/ruma-client-api/src/profile/set_avatar_url.rs new file mode 100644 index 00000000..3ff0107a --- /dev/null +++ b/crates/ruma-client-api/src/profile/set_avatar_url.rs @@ -0,0 +1,116 @@ +//! `PUT /_matrix/client/*/profile/{userId}/avatar_url` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3profileuseridavatar_url + + use ruma_api::ruma_api; + use ruma_identifiers::{MxcUri, UserId}; + + ruma_api! { + metadata: { + description: "Set the avatar URL of the user.", + method: PUT, + name: "set_avatar_url", + r0_path: "/_matrix/client/r0/profile/:user_id/avatar_url", + stable_path: "/_matrix/client/v3/profile/:user_id/avatar_url", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The user whose avatar URL will be set. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// The new avatar URL for the user. + /// + /// `None` is used to unset the avatar. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will result + /// in `None` here during deserialization. + #[cfg_attr( + feature = "compat", + serde( + default, + deserialize_with = "ruma_serde::empty_string_as_none", + serialize_with = "ruma_serde::none_as_empty_string" + ) + )] + #[cfg_attr( + not(feature = "compat"), + serde(skip_serializing_if = "Option::is_none") + )] + pub avatar_url: Option<&'a MxcUri>, + + /// The [BlurHash](https://blurha.sh) for the avatar pointed to by `avatar_url`. + /// + /// This uses the unstable prefix in + /// [MSC2448](https://github.com/matrix-org/matrix-doc/pull/2448). + #[cfg(feature = "unstable-msc2448")] + #[serde(rename = "xyz.amorgan.blurhash", skip_serializing_if = "Option::is_none")] + pub blurhash: Option<&'a str>, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID and avatar URL. + pub fn new(user_id: &'a UserId, avatar_url: Option<&'a MxcUri>) -> Self { + Self { + user_id, + avatar_url, + #[cfg(feature = "unstable-msc2448")] + blurhash: None, + } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } + + #[cfg(all(test, feature = "server"))] + mod tests { + use matches::assert_matches; + use ruma_api::IncomingRequest as _; + + use super::IncomingRequest; + + #[test] + fn deserialize_unset_request() { + assert_matches!( + IncomingRequest::try_from_http_request( + http::Request::builder() + .method("PUT") + .uri("https://bar.org/_matrix/client/r0/profile/@foo:bar.org/avatar_url") + .body(&[] as &[u8]).unwrap(), + &["@foo:bar.org"], + ).unwrap(), + IncomingRequest { user_id, avatar_url: None, .. } if user_id == "@foo:bar.org" + ); + + #[cfg(feature = "compat")] + assert_matches!( + IncomingRequest::try_from_http_request( + http::Request::builder() + .method("PUT") + .uri("https://bar.org/_matrix/client/r0/profile/@foo:bar.org/avatar_url") + .body(serde_json::to_vec(&serde_json::json!({ "avatar_url": "" })).unwrap()) + .unwrap(), + &["@foo:bar.org"], + ).unwrap(), + IncomingRequest { user_id, avatar_url: None, .. } if user_id == "@foo:bar.org" + ); + } + } +} diff --git a/crates/ruma-client-api/src/profile/set_display_name.rs b/crates/ruma-client-api/src/profile/set_display_name.rs new file mode 100644 index 00000000..4646b6ad --- /dev/null +++ b/crates/ruma-client-api/src/profile/set_display_name.rs @@ -0,0 +1,52 @@ +//! `PUT /_matrix/client/*/profile/{userId}/displayname` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3profileuseriddisplayname + + use ruma_api::ruma_api; + use ruma_identifiers::UserId; + + ruma_api! { + metadata: { + description: "Set the display name of the user.", + method: PUT, + name: "set_display_name", + r0_path: "/_matrix/client/r0/profile/:user_id/displayname", + stable_path: "/_matrix/client/v3/profile/:user_id/displayname", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The user whose display name will be set. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// The new display name for the user. + #[serde(skip_serializing_if = "Option::is_none")] + pub displayname: Option<&'a str>, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID and display name. + pub fn new(user_id: &'a UserId, displayname: Option<&'a str>) -> Self { + Self { user_id, displayname } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0/push.rs b/crates/ruma-client-api/src/push.rs similarity index 100% rename from crates/ruma-client-api/src/r0/push.rs rename to crates/ruma-client-api/src/push.rs diff --git a/crates/ruma-client-api/src/push/delete_pushrule.rs b/crates/ruma-client-api/src/push/delete_pushrule.rs new file mode 100644 index 00000000..23b6f9d5 --- /dev/null +++ b/crates/ruma-client-api/src/push/delete_pushrule.rs @@ -0,0 +1,57 @@ +//! `DELETE /_matrix/client/*/pushrules/{scope}/{kind}/{ruleId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#delete_matrixclientv3pushrulesscopekindruleid + + use ruma_api::ruma_api; + + use crate::push::RuleKind; + + ruma_api! { + metadata: { + description: "This endpoint removes the push rule defined in the path.", + method: DELETE, + name: "delete_pushrule", + r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id", + stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The scope to delete from. 'global' to specify global rules. + #[ruma_api(path)] + pub scope: &'a str, + + /// The kind of rule + #[ruma_api(path)] + pub kind: RuleKind, + + /// The identifier for the rule. + #[ruma_api(path)] + pub rule_id: &'a str, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given scope, kind and rule ID. + pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { + Self { scope, kind, rule_id } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/push/get_notifications.rs b/crates/ruma-client-api/src/push/get_notifications.rs new file mode 100644 index 00000000..5224472b --- /dev/null +++ b/crates/ruma-client-api/src/push/get_notifications.rs @@ -0,0 +1,117 @@ +//! `GET /_matrix/client/*/notifications` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3notifications + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_common::{push::Action, MilliSecondsSinceUnixEpoch}; + use ruma_events::AnySyncRoomEvent; + use ruma_identifiers::RoomId; + use ruma_serde::Raw; + use serde::{Deserialize, Serialize}; + + ruma_api! { + metadata: { + description: "Paginate through the list of events that the user has been, or would have been notified about.", + method: GET, + name: "get_notifications", + r0_path: "/_matrix/client/r0/notifications", + stable_path: "/_matrix/client/v3/notifications", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: { + /// Pagination token given to retrieve the next set of events. + #[ruma_api(query)] + #[serde(skip_serializing_if = "Option::is_none")] + pub from: Option<&'a str>, + + /// Limit on the number of events to return in this request. + #[ruma_api(query)] + #[serde(skip_serializing_if = "Option::is_none")] + pub limit: Option, + + /// Allows basic filtering of events returned. + /// + /// Supply "highlight" to return only events where the notification had the 'highlight' + /// tweak set. + #[ruma_api(query)] + #[serde(skip_serializing_if = "Option::is_none")] + pub only: Option<&'a str>, + } + + response: { + /// The token to supply in the from param of the next /notifications request in order to + /// request more events. + /// + /// If this is absent, there are no more results. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_token: Option, + + + /// The list of events that triggered notifications. + pub notifications: Vec, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates an empty `Request`. + pub fn new() -> Self { + Default::default() + } + } + + impl Response { + /// Creates a new `Response` with the given notifications. + pub fn new(notifications: Vec) -> Self { + Self { next_token: None, notifications } + } + } + + /// Represents a notification. + #[derive(Clone, Debug, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Notification { + /// The actions to perform when the conditions for this rule are met. + pub actions: Vec, + + /// The event that triggered the notification. + pub event: Raw, + + /// The profile tag of the rule that matched this event. + #[serde(skip_serializing_if = "Option::is_none")] + pub profile_tag: Option, + + /// Indicates whether the user has sent a read receipt indicating that they have read this + /// message. + pub read: bool, + + /// The ID of the room in which the event was posted. + pub room_id: Box, + + /// The time at which the event notification was sent. + pub ts: MilliSecondsSinceUnixEpoch, + } + + impl Notification { + /// Creates a new `Notification` with the given actions, event, read flag, room ID and + /// timestamp. + pub fn new( + actions: Vec, + event: Raw, + read: bool, + room_id: Box, + ts: MilliSecondsSinceUnixEpoch, + ) -> Self { + Self { actions, event, profile_tag: None, read, room_id, ts } + } + } +} diff --git a/crates/ruma-client-api/src/push/get_pushers.rs b/crates/ruma-client-api/src/push/get_pushers.rs new file mode 100644 index 00000000..c6d371a4 --- /dev/null +++ b/crates/ruma-client-api/src/push/get_pushers.rs @@ -0,0 +1,147 @@ +//! `GET /_matrix/client/*/pushers` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3pushers + + use ruma_api::ruma_api; + use serde::{Deserialize, Serialize}; + + use crate::push::{PusherData, PusherKind}; + + ruma_api! { + metadata: { + description: "Gets all currently active pushers for the authenticated user.", + method: GET, + name: "get_pushers", + r0_path: "/_matrix/client/r0/pushers", + stable_path: "/_matrix/client/v3/pushers", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// An array containing the current pushers for the user. + pub pushers: Vec, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given pushers. + pub fn new(pushers: Vec) -> Self { + Self { pushers } + } + } + + /// Defines a pusher. + /// + /// To create an instance of this type, first create a `PusherInit` and convert it via + /// `Pusher::from` / `.into()`. + #[derive(Clone, Debug, Serialize, Deserialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Pusher { + /// A unique identifier for this pusher. + /// + /// The maximum allowed length is 512 bytes. + pub pushkey: String, + + /// The kind of the pusher. + pub kind: PusherKind, + + /// A reverse-DNS style identifier for the application. + /// + /// The maximum allowed length is 64 bytes. + pub app_id: String, + + /// A string that will allow the user to identify what application owns this pusher. + pub app_display_name: String, + + /// A string that will allow the user to identify what device owns this pusher. + pub device_display_name: String, + + /// Determines which set of device specific rules this pusher executes. + #[serde(skip_serializing_if = "Option::is_none")] + pub profile_tag: Option, + + /// The preferred language for receiving notifications (e.g. 'en' or 'en-US') + pub lang: String, + + /// Information for the pusher implementation itself. + pub data: PusherData, + } + + /// Initial set of fields of `Pusher`. + /// + /// This struct will not be updated even if additional fields are added to `Pusher` in a new + /// (non-breaking) release of the Matrix specification. + #[derive(Debug)] + #[allow(clippy::exhaustive_structs)] + pub struct PusherInit { + /// A unique identifier for this pusher. + /// + /// The maximum allowed length is 512 bytes. + pub pushkey: String, + + /// The kind of the pusher. + pub kind: PusherKind, + + /// A reverse-DNS style identifier for the application. + /// + /// The maximum allowed length is 64 bytes. + pub app_id: String, + + /// A string that will allow the user to identify what application owns this pusher. + pub app_display_name: String, + + /// A string that will allow the user to identify what device owns this pusher. + pub device_display_name: String, + + /// Determines which set of device-specific rules this pusher executes. + pub profile_tag: Option, + + /// The preferred language for receiving notifications (e.g. 'en' or 'en-US'). + pub lang: String, + + /// Information for the pusher implementation itself. + pub data: PusherData, + } + + impl From for Pusher { + fn from(init: PusherInit) -> Self { + let PusherInit { + pushkey, + kind, + app_id, + app_display_name, + device_display_name, + profile_tag, + lang, + data, + } = init; + Self { + pushkey, + kind, + app_id, + app_display_name, + device_display_name, + profile_tag, + lang, + data, + } + } + } +} diff --git a/crates/ruma-client-api/src/push/get_pushrule.rs b/crates/ruma-client-api/src/push/get_pushrule.rs new file mode 100644 index 00000000..5b85d28b --- /dev/null +++ b/crates/ruma-client-api/src/push/get_pushrule.rs @@ -0,0 +1,60 @@ +//! `GET /_matrix/client/*/pushrules/{scope}/{kind}/{ruleId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3pushrulesscopekindruleid + + use ruma_api::ruma_api; + + use crate::push::{PushRule, RuleKind}; + + ruma_api! { + metadata: { + description: "Retrieve a single specified push rule.", + method: GET, + name: "get_pushrule", + r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id", + stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The scope to fetch rules from. 'global' to specify global rules. + #[ruma_api(path)] + pub scope: &'a str, + + /// The kind of rule. + #[ruma_api(path)] + pub kind: RuleKind, + + /// The identifier for the rule. + #[ruma_api(path)] + pub rule_id: &'a str, + } + + response: { + /// The specific push rule. + #[ruma_api(body)] + pub rule: PushRule, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given scope, rule kind and rule ID. + pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { + Self { scope, kind, rule_id } + } + } + + impl Response { + /// Creates a new `Response` with the given rule. + pub fn new(rule: PushRule) -> Self { + Self { rule } + } + } +} diff --git a/crates/ruma-client-api/src/push/get_pushrule_actions.rs b/crates/ruma-client-api/src/push/get_pushrule_actions.rs new file mode 100644 index 00000000..937b7ba4 --- /dev/null +++ b/crates/ruma-client-api/src/push/get_pushrule_actions.rs @@ -0,0 +1,60 @@ +//! `GET /_matrix/client/*/pushrules/{scope}/{kind}/{ruleId}/actions` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3pushrulesscopekindruleidactions + + use ruma_api::ruma_api; + use ruma_common::push::Action; + + use crate::push::RuleKind; + + ruma_api! { + metadata: { + description: "This endpoint get the actions for the specified push rule.", + method: GET, + name: "get_pushrule_actions", + r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id/actions", + stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id/actions", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The scope to fetch a rule from. 'global' to specify global rules. + #[ruma_api(path)] + pub scope: &'a str, + + /// The kind of rule + #[ruma_api(path)] + pub kind: RuleKind, + + /// The identifier for the rule. + #[ruma_api(path)] + pub rule_id: &'a str, + } + + response: { + /// The actions to perform for this rule. + pub actions: Vec, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given scope, kind and rule ID. + pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { + Self { scope, kind, rule_id } + } + } + + impl Response { + /// Creates a new `Response` with the given actions. + pub fn new(actions: Vec) -> Self { + Self { actions } + } + } +} diff --git a/crates/ruma-client-api/src/push/get_pushrule_enabled.rs b/crates/ruma-client-api/src/push/get_pushrule_enabled.rs new file mode 100644 index 00000000..53007e05 --- /dev/null +++ b/crates/ruma-client-api/src/push/get_pushrule_enabled.rs @@ -0,0 +1,59 @@ +//! `GET /_matrix/client/*/pushrules/{scope}/{kind}/{ruleId}/enabled` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3pushrulesscopekindruleidenabled + + use ruma_api::ruma_api; + + use crate::push::RuleKind; + + ruma_api! { + metadata: { + description: "This endpoint gets whether the specified push rule is enabled.", + method: GET, + name: "get_pushrule_enabled", + r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id/enabled", + stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id/enabled", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The scope to fetch a rule from. 'global' to specify global rules. + #[ruma_api(path)] + pub scope: &'a str, + + /// The kind of rule + #[ruma_api(path)] + pub kind: RuleKind, + + /// The identifier for the rule. + #[ruma_api(path)] + pub rule_id: &'a str, + } + + response: { + /// Whether the push rule is enabled or not. + pub enabled: bool, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given scope, rule kind and rule ID. + pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { + Self { scope, kind, rule_id } + } + } + + impl Response { + /// Creates a new `Response` with the given enabled flag. + pub fn new(enabled: bool) -> Self { + Self { enabled } + } + } +} diff --git a/crates/ruma-client-api/src/push/get_pushrules_all.rs b/crates/ruma-client-api/src/push/get_pushrules_all.rs new file mode 100644 index 00000000..25322e65 --- /dev/null +++ b/crates/ruma-client-api/src/push/get_pushrules_all.rs @@ -0,0 +1,47 @@ +//! `GET /_matrix/client/*/pushrules/` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3pushrules + + use ruma_api::ruma_api; + use ruma_common::push::Ruleset; + + ruma_api! { + metadata: { + description: "Retrieve all push rulesets for this user.", + method: GET, + name: "get_pushrules_all", + r0_path: "/_matrix/client/r0/pushrules/", + stable_path: "/_matrix/client/v3/pushrules/", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// The global ruleset. + pub global: Ruleset, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given global ruleset. + pub fn new(global: Ruleset) -> Self { + Self { global } + } + } +} diff --git a/crates/ruma-client-api/src/push/get_pushrules_global_scope.rs b/crates/ruma-client-api/src/push/get_pushrules_global_scope.rs new file mode 100644 index 00000000..0d9d8ea8 --- /dev/null +++ b/crates/ruma-client-api/src/push/get_pushrules_global_scope.rs @@ -0,0 +1,48 @@ +//! `GET /_matrix/client/*/pushrules/global/` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3pushrules + + use ruma_api::ruma_api; + use ruma_common::push::Ruleset; + + ruma_api! { + metadata: { + description: "Retrieve all push rulesets in the global scope for this user.", + method: GET, + name: "get_pushrules_global_scope", + r0_path: "/_matrix/client/r0/pushrules/global/", + stable_path: "/_matrix/client/v3/pushrules/global/", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// The global ruleset. + #[ruma_api(body)] + pub global: Ruleset, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given global ruleset. + pub fn new(global: Ruleset) -> Self { + Self { global } + } + } +} diff --git a/crates/ruma-client-api/src/push/set_pusher.rs b/crates/ruma-client-api/src/push/set_pusher.rs new file mode 100644 index 00000000..faf26f6c --- /dev/null +++ b/crates/ruma-client-api/src/push/set_pusher.rs @@ -0,0 +1,158 @@ +//! `POST /_matrix/client/*/pushers/set` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3pushersset + + use ruma_api::ruma_api; + use serde::{Deserialize, Serialize}; + + use crate::push::{PusherData, PusherKind}; + + ruma_api! { + metadata: { + description: "This endpoint allows the creation, modification and deletion of pushers for this user ID.", + method: POST, + name: "set_pusher", + r0_path: "/_matrix/client/r0/pushers/set", + stable_path: "/_matrix/client/v3/pushers/set", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The pusher to configure. + #[serde(flatten)] + pub pusher: Pusher, + + /// Controls if another pusher with the same pushkey and app id should be created. + /// + /// Defaults to `false`. See the spec for more details. + #[serde(default, skip_serializing_if = "ruma_serde::is_default")] + pub append: bool, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl Request { + /// Creates a new `Request` with the given pusher. + pub fn new(pusher: Pusher) -> Self { + Self { pusher, append: false } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } + + /// Defines a pusher. + /// + /// To create an instance of this type, first create a `PusherInit` and convert it via + /// `Pusher::from` / `.into()`. + #[derive(Clone, Debug, Serialize, Deserialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Pusher { + /// A unique identifier for this pusher. + /// + /// The maximum allowed length is 512 bytes. + pub pushkey: String, + + /// The kind of the pusher. + /// + /// `None` deletes the pusher. + pub kind: Option, + + /// A reverse-DNS style identifier for the application. + /// + /// The maximum allowed length is 64 bytes. + pub app_id: String, + + /// A string that will allow the user to identify what application owns this pusher. + pub app_display_name: String, + + /// A string that will allow the user to identify what device owns this pusher. + pub device_display_name: String, + + /// Determines which set of device specific rules this pusher executes. + #[serde(skip_serializing_if = "Option::is_none")] + pub profile_tag: Option, + + /// The preferred language for receiving notifications (e.g. 'en' or 'en-US') + pub lang: String, + + /// Information for the pusher implementation itself. + pub data: PusherData, + } + + /// Initial set of fields of `Pusher`. + /// + /// This struct will not be updated even if additional fields are added to `Pusher` in a new + /// (non-breaking) release of the Matrix specification. + #[derive(Debug)] + #[allow(clippy::exhaustive_structs)] + pub struct PusherInit { + /// A unique identifier for this pusher. + /// + /// The maximum allowed length is 512 bytes. + pub pushkey: String, + + /// The kind of the pusher. + /// + /// `None` deletes the pusher. + pub kind: Option, + + /// A reverse-DNS style identifier for the application. + /// + /// The maximum allowed length is 64 bytes. + pub app_id: String, + + /// A string that will allow the user to identify what application owns this pusher. + pub app_display_name: String, + + /// A string that will allow the user to identify what device owns this pusher. + pub device_display_name: String, + + /// Determines which set of device specific rules this pusher executes. + pub profile_tag: Option, + + /// The preferred language for receiving notifications (e.g. 'en' or 'en-US') + pub lang: String, + + /// Information for the pusher implementation itself. + pub data: PusherData, + } + + impl From for Pusher { + fn from(init: PusherInit) -> Self { + let PusherInit { + pushkey, + kind, + app_id, + app_display_name, + device_display_name, + profile_tag, + lang, + data, + } = init; + Self { + pushkey, + kind, + app_id, + app_display_name, + device_display_name, + profile_tag, + lang, + data, + } + } + } +} diff --git a/crates/ruma-client-api/src/push/set_pushrule.rs b/crates/ruma-client-api/src/push/set_pushrule.rs new file mode 100644 index 00000000..02324f45 --- /dev/null +++ b/crates/ruma-client-api/src/push/set_pushrule.rs @@ -0,0 +1,99 @@ +//! `PUT /_matrix/client/*/pushrules/{scope}/{kind}/{ruleId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3pushrulesscopekindruleid + + use ruma_api::ruma_api; + use ruma_common::push::{Action, PushCondition}; + + use crate::push::RuleKind; + + ruma_api! { + metadata: { + description: "This endpoint allows the creation, modification and deletion of pushers for this user ID.", + method: PUT, + name: "set_pushrule", + r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id", + stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The scope to set the rule in. 'global' to specify global rules. + #[ruma_api(path)] + pub scope: &'a str, + + /// The kind of rule + #[ruma_api(path)] + pub kind: RuleKind, + + /// The identifier for the rule. + #[ruma_api(path)] + pub rule_id: &'a str, + + /// Use 'before' with a rule_id as its value to make the new rule the next-most important + /// rule with respect to the given user defined rule. + #[ruma_api(query)] + pub before: Option<&'a str>, + + /// This makes the new rule the next-less important rule relative to the given user defined + /// rule. + #[ruma_api(query)] + pub after: Option<&'a str>, + + /// The actions to perform when this rule is matched. + pub actions: &'a [Action], + + /// The conditions that must hold true for an event in order for a rule to be applied to an + /// event. + /// + /// A rule with no conditions always matches. Only applicable to underride and override + /// rules, empty Vec otherwise. + #[serde(default, skip_serializing_if = "<[_]>::is_empty")] + pub conditions: &'a [PushCondition], + + /// The glob-style pattern to match against. + /// + /// Only applicable to content rules. + #[serde(skip_serializing_if = "Option::is_none")] + pub pattern: Option<&'a str>, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given scope, rule kind, rule ID and actions. + pub fn new( + scope: &'a str, + kind: RuleKind, + rule_id: &'a str, + actions: &'a [Action], + ) -> Self { + Self { + scope, + kind, + rule_id, + before: None, + after: None, + actions, + conditions: &[], + pattern: None, + } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/push/set_pushrule_actions.rs b/crates/ruma-client-api/src/push/set_pushrule_actions.rs new file mode 100644 index 00000000..7963099e --- /dev/null +++ b/crates/ruma-client-api/src/push/set_pushrule_actions.rs @@ -0,0 +1,61 @@ +//! `PUT /_matrix/client/*/pushrules/{scope}/{kind}/{ruleId}/actions` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3pushrulesscopekindruleidactions + + use ruma_api::ruma_api; + use ruma_common::push::Action; + + use crate::push::RuleKind; + + ruma_api! { + metadata: { + description: "This endpoint allows clients to change the actions of a push rule. This can be used to change the actions of builtin rules.", + method: PUT, + name: "set_pushrule_actions", + r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id/actions", + stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id/actions", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The scope to fetch a rule from. 'global' to specify global rules. + #[ruma_api(path)] + pub scope: &'a str, + + /// The kind of rule + #[ruma_api(path)] + pub kind: RuleKind, + + /// The identifier for the rule. + #[ruma_api(path)] + pub rule_id: &'a str, + + /// The actions to perform for this rule + pub actions: Vec, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given scope, rule kind, rule ID and actions. + pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str, actions: Vec) -> Self { + Self { scope, kind, rule_id, actions } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/push/set_pushrule_enabled.rs b/crates/ruma-client-api/src/push/set_pushrule_enabled.rs new file mode 100644 index 00000000..31d7a334 --- /dev/null +++ b/crates/ruma-client-api/src/push/set_pushrule_enabled.rs @@ -0,0 +1,70 @@ +//! `PUT /_matrix/client/*/pushrules/{scope}/{kind}/{ruleId}/enabled` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3pushrulesscopekindruleidenabled + + use ruma_api::ruma_api; + + use crate::push::RuleKind; + + ruma_api! { + metadata: { + description: "This endpoint allows clients to enable or disable the specified push rule.", + method: PUT, + name: "set_pushrule_enabled", + r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id/enabled", + stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id/enabled", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The scope to fetch a rule from. 'global' to specify global rules. + #[ruma_api(path)] + pub scope: &'a str, + + /// The kind of rule + #[ruma_api(path)] + pub kind: RuleKind, + + /// The identifier for the rule. + #[ruma_api(path)] + pub rule_id: &'a str, + + /// Whether the push rule is enabled or not. + pub enabled: bool, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given scope, rule kind, rule ID and enabled flag. + pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str, enabled: bool) -> Self { + Self { scope, kind, rule_id, enabled } + } + + /// Creates a new `Request` to enable the given rule. + pub fn enable(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { + Self::new(scope, kind, rule_id, true) + } + + /// Creates a new `Request` to disable the given rule. + pub fn disable(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { + Self::new(scope, kind, rule_id, false) + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0.rs b/crates/ruma-client-api/src/r0.rs deleted file mode 100644 index fad328e9..00000000 --- a/crates/ruma-client-api/src/r0.rs +++ /dev/null @@ -1,36 +0,0 @@ -//! Endpoints for the r0.x.x versions of the client API specification. - -pub mod account; -pub mod alias; -pub mod appservice; -pub mod backup; -pub mod capabilities; -pub mod config; -pub mod context; -pub mod device; -pub mod directory; -pub mod filter; -pub mod keys; -pub mod knock; -pub mod media; -pub mod membership; -pub mod message; -pub mod presence; -pub mod profile; -pub mod push; -pub mod read_marker; -pub mod receipt; -pub mod redact; -pub mod room; -pub mod search; -pub mod server; -pub mod session; -pub mod state; -pub mod sync; -pub mod tag; -pub mod thirdparty; -pub mod to_device; -pub mod typing; -pub mod uiaa; -pub mod user_directory; -pub mod voip; diff --git a/crates/ruma-client-api/src/r0/account/add_3pid.rs b/crates/ruma-client-api/src/r0/account/add_3pid.rs deleted file mode 100644 index 4e474d78..00000000 --- a/crates/ruma-client-api/src/r0/account/add_3pid.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! [POST /_matrix/client/r0/account/3pid/add](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-3pid-add) - -use ruma_api::ruma_api; -use ruma_identifiers::{ClientSecret, SessionId}; - -use crate::r0::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; - -ruma_api! { - metadata: { - description: "Add contact information to a user's account", - method: POST, - name: "add_3pid", - r0_path: "/_matrix/client/r0/account/3pid/add", - stable_path: "/_matrix/client/v3/account/3pid/add", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// Additional information for the User-Interactive Authentication API. - #[serde(skip_serializing_if = "Option::is_none")] - pub auth: Option>, - - /// Client-generated secret string used to protect this session. - pub client_secret: &'a ClientSecret, - - /// The session identifier given by the identity server. - pub sid: &'a SessionId, - } - - #[derive(Default)] - response: {} - - error: UiaaResponse -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given client secret and session identifier. - pub fn new(client_secret: &'a ClientSecret, sid: &'a SessionId) -> Self { - Self { auth: None, client_secret, sid } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/account/bind_3pid.rs b/crates/ruma-client-api/src/r0/account/bind_3pid.rs deleted file mode 100644 index a3f45ae7..00000000 --- a/crates/ruma-client-api/src/r0/account/bind_3pid.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! [POST /_matrix/client/r0/account/3pid/bind](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-3pid-bind) - -use ruma_api::ruma_api; -use ruma_identifiers::{ClientSecret, SessionId}; - -use super::{IdentityServerInfo, IncomingIdentityServerInfo}; - -ruma_api! { - metadata: { - description: "Bind a 3PID to a user's account on an identity server", - method: POST, - name: "bind_3pid", - r0_path: "/_matrix/client/r0/account/3pid/bind", - stable_path: "/_matrix/client/v3/account/3pid/bind", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// Client-generated secret string used to protect this session. - pub client_secret: &'a ClientSecret, - - /// The ID server to send the onward request to as a hostname with an - /// appended colon and port number if the port is not the default. - #[serde(flatten)] - pub identity_server_info: IdentityServerInfo<'a>, - - /// The session identifier given by the identity server. - pub sid: &'a SessionId, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given client secret, identity server information and - /// session identifier. - pub fn new( - client_secret: &'a ClientSecret, - identity_server_info: IdentityServerInfo<'a>, - sid: &'a SessionId, - ) -> Self { - Self { client_secret, identity_server_info, sid } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/account/change_password.rs b/crates/ruma-client-api/src/r0/account/change_password.rs deleted file mode 100644 index 31cbc288..00000000 --- a/crates/ruma-client-api/src/r0/account/change_password.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! [POST /_matrix/client/r0/account/password](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-password) - -use ruma_api::ruma_api; - -use crate::r0::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; - -ruma_api! { - metadata: { - description: "Change the password of the current user's account.", - method: POST, - name: "change_password", - r0_path: "/_matrix/client/r0/account/password", - stable_path: "/_matrix/client/v3/account/password", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The new password for the account. - pub new_password: &'a str, - - /// True to revoke the user's other access tokens, and their associated devices if the - /// request succeeds. - /// - /// Defaults to true. - /// - /// When false, the server can still take advantage of the soft logout method for the user's - /// remaining devices. - #[serde(default = "ruma_serde::default_true", skip_serializing_if = "ruma_serde::is_true")] - pub logout_devices: bool, - - /// Additional authentication information for the user-interactive authentication API. - #[serde(skip_serializing_if = "Option::is_none")] - pub auth: Option>, - } - - #[derive(Default)] - response: {} - - error: UiaaResponse -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given password. - pub fn new(new_password: &'a str) -> Self { - Self { new_password, logout_devices: true, auth: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/account/check_registration_token_validity.rs b/crates/ruma-client-api/src/r0/account/check_registration_token_validity.rs deleted file mode 100644 index 672fb28c..00000000 --- a/crates/ruma-client-api/src/r0/account/check_registration_token_validity.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! [GET /_matrix/client/v1/register/m.login.registration_token/validity](https://spec.matrix.org/unstable/client-server-api/#get_matrixclientv1registermloginregistration_tokenvalidity) - -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "Checks to see if the given registration token is valid.", - method: GET, - name: "check_registration_token_validity", - unstable_path: "/_matrix/client/unstable/org.matrix.msc3231/register/org.matrix.msc3231.login.registration_token/validity", - stable_path: "/_matrix/client/v1/register/m.login.registration_token/validity", - rate_limited: true, - authentication: None, - added: 1.2, - } - - request: { - /// The registration token to check the validity of. - #[ruma_api(query)] - pub registration_token: &'a str, - } - - response: { - /// A flag to indicate that the registration token is valid. - pub valid: bool, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given registration token. - pub fn new(registration_token: &'a str) -> Self { - Self { registration_token } - } -} - -impl Response { - /// Creates a new `Response` with the given validity flag. - pub fn new(valid: bool) -> Self { - Self { valid } - } -} diff --git a/crates/ruma-client-api/src/r0/account/deactivate.rs b/crates/ruma-client-api/src/r0/account/deactivate.rs deleted file mode 100644 index 6d26e00c..00000000 --- a/crates/ruma-client-api/src/r0/account/deactivate.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! [POST /_matrix/client/r0/account/deactivate](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-deactivate) - -use ruma_api::ruma_api; - -use super::ThirdPartyIdRemovalStatus; -use crate::r0::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; - -ruma_api! { - metadata: { - description: "Deactivate the current user's account.", - method: POST, - name: "deactivate", - r0_path: "/_matrix/client/r0/account/deactivate", - stable_path: "/_matrix/client/v3/account/deactivate", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: { - /// Additional authentication information for the user-interactive authentication API. - #[serde(skip_serializing_if = "Option::is_none")] - pub auth: Option>, - - /// Identity server from which to unbind the user's third party - /// identifier. - #[serde(skip_serializing_if = "Option::is_none")] - pub id_server: Option<&'a str>, - } - - response: { - /// Result of unbind operation. - pub id_server_unbind_result: ThirdPartyIdRemovalStatus, - } - - error: UiaaResponse -} - -impl Request<'_> { - /// Creates an empty `Request`. - pub fn new() -> Self { - Default::default() - } -} - -impl Response { - /// Creates a new `Response` with the given unbind result. - pub fn new(id_server_unbind_result: ThirdPartyIdRemovalStatus) -> Self { - Self { id_server_unbind_result } - } -} diff --git a/crates/ruma-client-api/src/r0/account/delete_3pid.rs b/crates/ruma-client-api/src/r0/account/delete_3pid.rs deleted file mode 100644 index f2bdc1c3..00000000 --- a/crates/ruma-client-api/src/r0/account/delete_3pid.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! [POST /_matrix/client/r0/account/3pid/delete](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-3pid-delete) - -use ruma_api::ruma_api; -use ruma_common::thirdparty::Medium; - -use super::ThirdPartyIdRemovalStatus; - -ruma_api! { - metadata: { - description: "Delete a 3PID from a user's account on an identity server.", - method: POST, - name: "delete_3pid", - r0_path: "/_matrix/client/r0/account/3pid/delete", - stable_path: "/_matrix/client/v3/account/3pid/delete", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// Identity server to delete from. - #[serde(skip_serializing_if = "Option::is_none")] - pub id_server: Option<&'a str>, - - /// Medium of the 3PID to be removed. - pub medium: Medium, - - /// Third-party address being removed. - pub address: &'a str, - } - - response: { - /// Result of unbind operation. - pub id_server_unbind_result: ThirdPartyIdRemovalStatus, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given medium and address. - pub fn new(medium: Medium, address: &'a str) -> Self { - Self { id_server: None, medium, address } - } -} diff --git a/crates/ruma-client-api/src/r0/account/get_3pids.rs b/crates/ruma-client-api/src/r0/account/get_3pids.rs deleted file mode 100644 index 958bdae2..00000000 --- a/crates/ruma-client-api/src/r0/account/get_3pids.rs +++ /dev/null @@ -1,43 +0,0 @@ -//! [GET /_matrix/client/r0/account/3pid](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-account-3pid) - -use ruma_api::ruma_api; -use ruma_common::thirdparty::ThirdPartyIdentifier; - -ruma_api! { - metadata: { - description: "Get a list of 3rd party contacts associated with the user's account.", - method: GET, - name: "get_3pids", - r0_path: "/_matrix/client/r0/account/3pid", - stable_path: "/_matrix/client/v3/account/3pid", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// A list of third party identifiers the homeserver has associated with the user's account. - #[serde(default)] - #[cfg_attr(not(feature = "compat"), serde(skip_serializing_if = "Vec::is_empty"))] - pub threepids: Vec, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given third party identifiers. - pub fn new(threepids: Vec) -> Self { - Self { threepids } - } -} diff --git a/crates/ruma-client-api/src/r0/account/get_username_availability.rs b/crates/ruma-client-api/src/r0/account/get_username_availability.rs deleted file mode 100644 index 90e0c6a5..00000000 --- a/crates/ruma-client-api/src/r0/account/get_username_availability.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! [GET /_matrix/client/r0/register/available](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-register-available) - -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "Checks to see if a username is available, and valid, for the server.", - method: GET, - name: "get_username_availability", - r0_path: "/_matrix/client/r0/register/available", - stable_path: "/_matrix/client/v3/register/available", - rate_limited: true, - authentication: None, - added: 1.0, - } - - request: { - /// The username to check the availability of. - #[ruma_api(query)] - pub username: &'a str, - } - - response: { - /// A flag to indicate that the username is available. - /// This should always be true when the server replies with 200 OK. - pub available: bool, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given username. - pub fn new(username: &'a str) -> Self { - Self { username } - } -} - -impl Response { - /// Creates a new `Response` with the given availability flag. - pub fn new(available: bool) -> Self { - Self { available } - } -} diff --git a/crates/ruma-client-api/src/r0/account/register.rs b/crates/ruma-client-api/src/r0/account/register.rs deleted file mode 100644 index 77bcce29..00000000 --- a/crates/ruma-client-api/src/r0/account/register.rs +++ /dev/null @@ -1,140 +0,0 @@ -//! [POST /_matrix/client/r0/register](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-register) - -use ruma_api::ruma_api; -use ruma_identifiers::{DeviceId, UserId}; -use serde::{Deserialize, Serialize}; - -use crate::r0::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; - -ruma_api! { - metadata: { - description: "Register an account on this homeserver.", - method: POST, - name: "register", - r0_path: "/_matrix/client/r0/register", - stable_path: "/_matrix/client/v3/register", - rate_limited: true, - authentication: None, - added: 1.0, - } - - #[derive(Default)] - request: { - /// The desired password for the account. - /// - /// May be empty for accounts that should not be able to log in again - /// with a password, e.g., for guest or application service accounts. - #[serde(skip_serializing_if = "Option::is_none")] - pub password: Option<&'a str>, - - /// Localpart of the desired Matrix ID. - /// - /// If omitted, the homeserver MUST generate a Matrix ID local part. - #[serde(skip_serializing_if = "Option::is_none")] - pub username: Option<&'a str>, - - /// ID of the client device. - /// - /// If this does not correspond to a known client device, a new device will be created. - /// The server will auto-generate a device_id if this is not specified. - #[serde(skip_serializing_if = "Option::is_none")] - pub device_id: Option<&'a DeviceId>, - - /// A display name to assign to the newly-created device. - /// - /// Ignored if `device_id` corresponds to a known device. - #[serde(skip_serializing_if = "Option::is_none")] - pub initial_device_display_name: Option<&'a str>, - - /// Additional authentication information for the user-interactive authentication API. - /// - /// Note that this information is not used to define how the registered user should be - /// authenticated, but is instead used to authenticate the register call itself. - /// It should be left empty, or omitted, unless an earlier call returned an response - /// with status code 401. - #[serde(skip_serializing_if = "Option::is_none")] - pub auth: Option>, - - /// Kind of account to register - /// - /// Defaults to `User` if omitted. - #[ruma_api(query)] - #[serde(default, skip_serializing_if = "ruma_serde::is_default")] - pub kind: RegistrationKind, - - /// If `true`, an `access_token` and `device_id` should not be returned - /// from this call, therefore preventing an automatic login. - #[serde(default, skip_serializing_if = "ruma_serde::is_default")] - pub inhibit_login: bool, - - /// Login `type` used by Appservices. - /// - /// Appservices can [bypass the registration flows][admin] entirely by providing their - /// token in the header and setting this login `type` to `m.login.application_service`. - /// - /// [admin]: https://matrix.org/docs/spec/application_service/r0.1.2#server-admin-style-permissions - #[serde(rename = "type", skip_serializing_if = "Option::is_none")] - pub login_type: Option<&'a LoginType>, - } - - response: { - /// An access token for the account. - /// - /// This access token can then be used to authorize other requests. - #[serde(skip_serializing_if = "Option::is_none")] - pub access_token: Option, - - /// The fully-qualified Matrix ID that has been registered. - pub user_id: Box, - - /// ID of the registered device. - /// - /// Will be the same as the corresponding parameter in the request, if one was specified. - pub device_id: Option>, - } - - error: UiaaResponse -} - -impl Request<'_> { - /// Creates a new `Request` with all parameters defaulted. - pub fn new() -> Self { - Default::default() - } -} - -impl Response { - /// Creates a new `Response` with the given user ID. - pub fn new(user_id: Box) -> Self { - Self { access_token: None, user_id, device_id: None } - } -} - -/// The kind of account being registered. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -#[serde(rename_all = "snake_case")] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub enum RegistrationKind { - /// A guest account - /// - /// These accounts may have limited permissions and may not be supported by all servers. - Guest, - - /// A regular user account - User, -} - -impl Default for RegistrationKind { - fn default() -> Self { - Self::User - } -} - -/// The login type. -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub enum LoginType { - /// An appservice-specific login type - #[serde(rename = "m.login.application_service")] - ApplicationService, -} diff --git a/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_email.rs b/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_email.rs deleted file mode 100644 index bb5cc89d..00000000 --- a/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_email.rs +++ /dev/null @@ -1,75 +0,0 @@ -//! [POST /_matrix/client/r0/account/3pid/email/requestToken](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-3pid-email-requesttoken) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::{ClientSecret, SessionId}; - -use super::{IdentityServerInfo, IncomingIdentityServerInfo}; - -ruma_api! { - metadata: { - description: "Request a 3PID management token with a 3rd party email.", - method: POST, - name: "request_3pid_management_token_via_email", - r0_path: "/_matrix/client/r0/account/3pid/email/requestToken", - stable_path: "/_matrix/client/v3/account/3pid/email/requestToken", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// Client-generated secret string used to protect this session. - pub client_secret: &'a ClientSecret, - - /// The email address. - pub email: &'a str, - - /// Used to distinguish protocol level retries from requests to re-send the email. - pub send_attempt: UInt, - - /// Return URL for identity server to redirect the client back to. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_link: Option<&'a str>, - - /// Optional identity server hostname and access token. - /// - /// Deprecated since r0.6.0. - #[serde(flatten, skip_serializing_if = "Option::is_none")] - pub identity_server_info: Option>, - } - - response: { - /// The session identifier given by the identity server. - pub sid: Box, - - /// URL to submit validation token to. - /// - /// If omitted, verification happens without client. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will - /// result in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub submit_url: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the client secret, email and send-attempt counter. - pub fn new(client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt) -> Self { - Self { client_secret, email, send_attempt, next_link: None, identity_server_info: None } - } -} - -impl Response { - /// Creates a new `Response` with the given session identifier. - pub fn new(sid: Box) -> Self { - Self { sid, submit_url: None } - } -} diff --git a/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_msisdn.rs b/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_msisdn.rs deleted file mode 100644 index 15045f89..00000000 --- a/crates/ruma-client-api/src/r0/account/request_3pid_management_token_via_msisdn.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! [POST /_matrix/client/r0/account/3pid/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-3pid-msisdn-requesttoken) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::{ClientSecret, SessionId}; - -use super::{IdentityServerInfo, IncomingIdentityServerInfo}; - -ruma_api! { - metadata: { - description: "Request a 3PID management token with a phone number.", - method: POST, - name: "request_3pid_management_token_via_msisdn", - r0_path: "/_matrix/client/r0/account/3pid/msisdn/requestToken", - stable_path: "/_matrix/client/v3/account/3pid/msisdn/requestToken", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// Client-generated secret string used to protect this session. - pub client_secret: &'a ClientSecret, - - /// Two-letter ISO 3166 country code for the phone number. - pub country: &'a str, - - /// Phone number to validate. - pub phone_number: &'a str, - - /// Used to distinguish protocol level retries from requests to re-send the SMS. - pub send_attempt: UInt, - - /// Return URL for identity server to redirect the client back to. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_link: Option<&'a str>, - - /// Optional identity server hostname and access token. - /// - /// Deprecated since r0.6.0. - #[serde(flatten, skip_serializing_if = "Option::is_none")] - pub identity_server_info: Option>, - } - - response: { - /// The session identifier given by the identity server. - pub sid: Box, - - /// URL to submit validation token to. - /// - /// If omitted, verification happens without client. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will - /// result in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub submit_url: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given client secret, country code, phone number and - /// send-attempt counter. - pub fn new( - client_secret: &'a ClientSecret, - country: &'a str, - phone_number: &'a str, - send_attempt: UInt, - ) -> Self { - Self { - client_secret, - country, - phone_number, - send_attempt, - next_link: None, - identity_server_info: None, - } - } -} - -impl Response { - /// Creates a new `Response` with the given session identifier. - pub fn new(sid: Box) -> Self { - Self { sid, submit_url: None } - } -} diff --git a/crates/ruma-client-api/src/r0/account/request_openid_token.rs b/crates/ruma-client-api/src/r0/account/request_openid_token.rs deleted file mode 100644 index 0788646a..00000000 --- a/crates/ruma-client-api/src/r0/account/request_openid_token.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! [POST /_matrix/client/r0/user/{userId}/openid/request_token](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-user-userid-openid-request-token) - -use std::time::Duration; - -use ruma_api::ruma_api; -use ruma_common::authentication::TokenType; -use ruma_identifiers::{ServerName, UserId}; - -ruma_api! { - metadata: { - description: "Request an OpenID 1.0 token to verify identity with a third party.", - name: "request_openid_token", - method: POST, - r0_path: "/_matrix/client/r0/user/:user_id/openid/request_token", - stable_path: "/_matrix/client/v3/user/:user_id/openid/request_token", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// User ID of authenticated user. - #[ruma_api(path)] - pub user_id: &'a UserId, - } - - response: { - /// Access token for verifying user's identity. - pub access_token: String, - - /// Access token type. - pub token_type: TokenType, - - /// Homeserver domain for verification of user's identity. - pub matrix_server_name: Box, - - /// Seconds until token expiration. - #[serde(with = "ruma_serde::duration::secs")] - pub expires_in: Duration, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID. - pub fn new(user_id: &'a UserId) -> Self { - Self { user_id } - } -} - -impl Response { - /// Creates a new `Response` with the given access token, token type, server name and expiration - /// duration. - pub fn new( - access_token: String, - token_type: TokenType, - matrix_server_name: Box, - expires_in: Duration, - ) -> Self { - Self { access_token, token_type, matrix_server_name, expires_in } - } -} diff --git a/crates/ruma-client-api/src/r0/account/request_password_change_token_via_email.rs b/crates/ruma-client-api/src/r0/account/request_password_change_token_via_email.rs deleted file mode 100644 index ae77d43e..00000000 --- a/crates/ruma-client-api/src/r0/account/request_password_change_token_via_email.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! [POST /_matrix/client/r0/account/password/email/requestToken](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-password-email-requesttoken) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::{ClientSecret, SessionId}; - -use super::{IdentityServerInfo, IncomingIdentityServerInfo}; - -ruma_api! { - metadata: { - description: "Request that a password change token is sent to the given email address.", - method: POST, - name: "request_password_change_token_via_email", - r0_path: "/_matrix/client/r0/account/password/email/requestToken", - stable_path: "/_matrix/client/v3/account/password/email/requestToken", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// Client-generated secret string used to protect this session. - pub client_secret: &'a ClientSecret, - - /// The email address. - pub email: &'a str, - - /// Used to distinguish protocol level retries from requests to re-send the email. - pub send_attempt: UInt, - - /// Return URL for identity server to redirect the client back to. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_link: Option<&'a str>, - - /// Optional identity server hostname and access token. - /// - /// Deprecated since r0.6.0. - #[serde(flatten, skip_serializing_if = "Option::is_none")] - pub identity_server_info: Option>, - } - - response: { - /// The session identifier given by the identity server. - pub sid: Box, - - /// URL to submit validation token to. - /// - /// If omitted, verification happens without client. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will result - /// in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub submit_url: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given client secret, email address and send-attempt - /// counter. - pub fn new(client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt) -> Self { - Self { client_secret, email, send_attempt, next_link: None, identity_server_info: None } - } -} - -impl Response { - /// Creates a new `Response` with the given session identifier. - pub fn new(sid: Box) -> Self { - Self { sid, submit_url: None } - } -} diff --git a/crates/ruma-client-api/src/r0/account/request_password_change_token_via_msisdn.rs b/crates/ruma-client-api/src/r0/account/request_password_change_token_via_msisdn.rs deleted file mode 100644 index c1dda776..00000000 --- a/crates/ruma-client-api/src/r0/account/request_password_change_token_via_msisdn.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! [POST /_matrix/client/r0/account/password/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-password-msisdn-requesttoken) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::{ClientSecret, SessionId}; - -ruma_api! { - metadata: { - description: "Request that a password change token is sent to the given phone number.", - method: POST, - name: "request_password_change_token_via_msisdn", - r0_path: "/_matrix/client/r0/account/password/msisdn/requestToken", - stable_path: "/_matrix/client/v3/account/password/msisdn/requestToken", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// Client-generated secret string used to protect this session. - pub client_secret: &'a ClientSecret, - - /// Two-letter ISO 3166 country code for the phone number. - pub country: &'a str, - - /// Phone number to validate. - pub phone_number: &'a str, - - /// Used to distinguish protocol level retries from requests to re-send the SMS. - pub send_attempt: UInt, - - /// Return URL for identity server to redirect the client back to. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_link: Option<&'a str>, - } - - response: { - /// The session identifier given by the identity server. - pub sid: Box, - - /// URL to submit validation token to. - /// - /// If omitted, verification happens without client. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will result - /// in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub submit_url: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given client secret, country code, phone number and - /// send-attempt counter. - pub fn new( - client_secret: &'a ClientSecret, - country: &'a str, - phone_number: &'a str, - send_attempt: UInt, - ) -> Self { - Self { client_secret, country, phone_number, send_attempt, next_link: None } - } -} - -impl Response { - /// Creates a new `Response` with the given session identifier. - pub fn new(sid: Box) -> Self { - Self { sid, submit_url: None } - } -} diff --git a/crates/ruma-client-api/src/r0/account/request_registration_token_via_email.rs b/crates/ruma-client-api/src/r0/account/request_registration_token_via_email.rs deleted file mode 100644 index 66a06ae9..00000000 --- a/crates/ruma-client-api/src/r0/account/request_registration_token_via_email.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! [POST /_matrix/client/r0/register/email/requestToken](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-register-email-requesttoken) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::{ClientSecret, SessionId}; - -use super::{IdentityServerInfo, IncomingIdentityServerInfo}; - -ruma_api! { - metadata: { - description: "Request a registration token with a 3rd party email.", - method: POST, - name: "request_registration_token_via_email", - r0_path: "/_matrix/client/r0/register/email/requestToken", - stable_path: "/_matrix/client/v3/register/email/requestToken", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// Client-generated secret string used to protect this session. - pub client_secret: &'a ClientSecret, - - /// The email address. - pub email: &'a str, - - /// Used to distinguish protocol level retries from requests to re-send the email. - pub send_attempt: UInt, - - /// Return URL for identity server to redirect the client back to. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_link: Option<&'a str>, - - /// Optional identity server hostname and access token. - /// - /// Deprecated since r0.6.0. - #[serde(flatten, skip_serializing_if = "Option::is_none")] - pub identity_server_info: Option>, - } - - response: { - /// The session identifier given by the identity server. - pub sid: Box, - - /// URL to submit validation token to. - /// - /// If omitted, verification happens without client. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will result - /// in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub submit_url: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given client secret, email address and send-attempt - /// counter. - pub fn new(client_secret: &'a ClientSecret, email: &'a str, send_attempt: UInt) -> Self { - Self { client_secret, email, send_attempt, next_link: None, identity_server_info: None } - } -} - -impl Response { - /// Creates a new `Response` with the given session identifier. - pub fn new(sid: Box) -> Self { - Self { sid, submit_url: None } - } -} diff --git a/crates/ruma-client-api/src/r0/account/request_registration_token_via_msisdn.rs b/crates/ruma-client-api/src/r0/account/request_registration_token_via_msisdn.rs deleted file mode 100644 index 4cbc544f..00000000 --- a/crates/ruma-client-api/src/r0/account/request_registration_token_via_msisdn.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! [POST /_matrix/client/r0/register/msisdn/requestToken](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-register-msisdn-requesttoken) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::{ClientSecret, SessionId}; - -use super::{IdentityServerInfo, IncomingIdentityServerInfo}; - -ruma_api! { - metadata: { - description: "Request a registration token with a phone number.", - method: POST, - name: "request_registration_token_via_msisdn", - r0_path: "/_matrix/client/r0/register/msisdn/requestToken", - stable_path: "/_matrix/client/v3/register/msisdn/requestToken", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// Client-generated secret string used to protect this session. - pub client_secret: &'a ClientSecret, - - /// Two-letter ISO 3166 country code for the phone number. - pub country: &'a str, - - /// Phone number to validate. - pub phone_number: &'a str, - - /// Used to distinguish protocol level retries from requests to re-send the SMS. - pub send_attempt: UInt, - - /// Return URL for identity server to redirect the client back to. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_link: Option<&'a str>, - - /// Optional identity server hostname and access token. - /// - /// Deprecated since r0.6.0. - #[serde(flatten, skip_serializing_if = "Option::is_none")] - pub identity_server_info: Option>, - } - - response: { - /// The session identifier given by the identity server. - pub sid: Box, - - /// URL to submit validation token to. - /// - /// If omitted, verification happens without client. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will result - /// in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub submit_url: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given client secret, country code, phone number and - /// send-attempt counter. - pub fn new( - client_secret: &'a ClientSecret, - country: &'a str, - phone_number: &'a str, - send_attempt: UInt, - ) -> Self { - Self { - client_secret, - country, - phone_number, - send_attempt, - next_link: None, - identity_server_info: None, - } - } -} - -impl Response { - /// Creates a new `Response` with the given session identifier. - pub fn new(sid: Box) -> Self { - Self { sid, submit_url: None } - } -} diff --git a/crates/ruma-client-api/src/r0/account/unbind_3pid.rs b/crates/ruma-client-api/src/r0/account/unbind_3pid.rs deleted file mode 100644 index 7612119b..00000000 --- a/crates/ruma-client-api/src/r0/account/unbind_3pid.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! [POST /_matrix/client/r0/account/3pid/unbind](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-account-3pid-unbind) - -use ruma_api::ruma_api; -use ruma_common::thirdparty::Medium; - -use super::ThirdPartyIdRemovalStatus; - -ruma_api! { - metadata: { - description: "Unbind a 3PID from a user's account on an identity server.", - method: POST, - name: "unbind_3pid", - r0_path: "/_matrix/client/r0/account/3pid/unbind", - stable_path: "/_matrix/client/v3/account/3pid/unbind", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// Identity server to unbind from. - #[serde(skip_serializing_if = "Option::is_none")] - pub id_server: Option<&'a str>, - - /// Medium of the 3PID to be removed. - pub medium: Medium, - - /// Third-party address being removed. - pub address: &'a str, - } - - response: { - /// Result of unbind operation. - pub id_server_unbind_result: ThirdPartyIdRemovalStatus, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given medium and third-party address. - pub fn new(medium: Medium, address: &'a str) -> Self { - Self { id_server: None, medium, address } - } -} - -impl Response { - /// Creates a new `Response` with the given unbind result. - pub fn new(id_server_unbind_result: ThirdPartyIdRemovalStatus) -> Self { - Self { id_server_unbind_result } - } -} diff --git a/crates/ruma-client-api/src/r0/account/whoami.rs b/crates/ruma-client-api/src/r0/account/whoami.rs deleted file mode 100644 index f1aeed15..00000000 --- a/crates/ruma-client-api/src/r0/account/whoami.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! [GET /_matrix/client/r0/account/whoami](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-account-whoami) - -use ruma_api::ruma_api; -use ruma_identifiers::UserId; - -ruma_api! { - metadata: { - description: "Get information about the owner of a given access token.", - method: GET, - name: "whoami", - r0_path: "/_matrix/client/r0/account/whoami", - stable_path: "/_matrix/client/v3/account/whoami", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// The id of the user that owns the access token. - pub user_id: Box, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given user ID. - pub fn new(user_id: Box) -> Self { - Self { user_id } - } -} diff --git a/crates/ruma-client-api/src/r0/alias/create_alias.rs b/crates/ruma-client-api/src/r0/alias/create_alias.rs deleted file mode 100644 index 0eb64101..00000000 --- a/crates/ruma-client-api/src/r0/alias/create_alias.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! [PUT /_matrix/client/r0/directory/room/{roomAlias}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-directory-room-roomalias) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomAliasId, RoomId}; - -ruma_api! { - metadata: { - description: "Add an alias to a room.", - method: PUT, - name: "create_alias", - r0_path: "/_matrix/client/r0/directory/room/:room_alias", - stable_path: "/_matrix/client/v3/directory/room/:room_alias", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room alias to set. - #[ruma_api(path)] - pub room_alias: &'a RoomAliasId, - - /// The room ID to set. - pub room_id: &'a RoomId, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room alias and room id. - pub fn new(room_alias: &'a RoomAliasId, room_id: &'a RoomId) -> Self { - Self { room_alias, room_id } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/alias/delete_alias.rs b/crates/ruma-client-api/src/r0/alias/delete_alias.rs deleted file mode 100644 index 3cb0f75b..00000000 --- a/crates/ruma-client-api/src/r0/alias/delete_alias.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! [DELETE /_matrix/client/r0/directory/room/{roomAlias}](https://matrix.org/docs/spec/client_server/r0.6.1#delete-matrix-client-r0-directory-room-roomalias) - -use ruma_api::ruma_api; -use ruma_identifiers::RoomAliasId; - -ruma_api! { - metadata: { - description: "Remove an alias from a room.", - method: DELETE, - name: "delete_alias", - r0_path: "/_matrix/client/r0/directory/room/:room_alias", - stable_path: "/_matrix/client/v3/directory/room/:room_alias", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room alias to remove. - #[ruma_api(path)] - pub room_alias: &'a RoomAliasId, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room alias. - pub fn new(room_alias: &'a RoomAliasId) -> Self { - Self { room_alias } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/alias/get_alias.rs b/crates/ruma-client-api/src/r0/alias/get_alias.rs deleted file mode 100644 index 66cfce5e..00000000 --- a/crates/ruma-client-api/src/r0/alias/get_alias.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! [GET /_matrix/client/r0/directory/room/{roomAlias}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-directory-room-roomalias) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomAliasId, RoomId, ServerName}; - -ruma_api! { - metadata: { - description: "Resolve a room alias to a room ID.", - method: GET, - name: "get_alias", - r0_path: "/_matrix/client/r0/directory/room/:room_alias", - stable_path: "/_matrix/client/v3/directory/room/:room_alias", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// The room alias. - #[ruma_api(path)] - pub room_alias: &'a RoomAliasId, - } - - response: { - /// The room ID for this room alias. - pub room_id: Box, - - /// A list of servers that are aware of this room ID. - pub servers: Vec>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room alias id. - pub fn new(room_alias: &'a RoomAliasId) -> Self { - Self { room_alias } - } -} - -impl Response { - /// Creates a new `Response` with the given room id and servers - pub fn new(room_id: Box, servers: Vec>) -> Self { - Self { room_id, servers } - } -} diff --git a/crates/ruma-client-api/src/r0/appservice/set_room_visibility.rs b/crates/ruma-client-api/src/r0/appservice/set_room_visibility.rs deleted file mode 100644 index 7737db25..00000000 --- a/crates/ruma-client-api/src/r0/appservice/set_room_visibility.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! [PUT /_matrix/client/r0/directory/list/appservice/{networkId}/{roomId}](https://matrix.org/docs/spec/application_service/r0.1.2#put-matrix-client-r0-directory-list-appservice-networkid-roomid) - -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -use crate::r0::room::Visibility; - -ruma_api! { - metadata: { - description: "Updates the visibility of a given room on the application service's room directory.", - method: PUT, - name: "set_room_visibility", - r0_path: "/_matrix/client/r0/directory/list/appservice/:network_id/:room_id", - stable_path: "/_matrix/client/v3/directory/list/appservice/:network_id/:room_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The protocol (network) ID to update the room list for. - #[ruma_api(path)] - pub network_id: &'a str, - - /// The room ID to add to the directory. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// Whether the room should be visible (public) in the directory or not (private). - pub visibility: Visibility, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given network ID, room ID and visibility. - pub fn new(network_id: &'a str, room_id: &'a RoomId, visibility: Visibility) -> Self { - Self { network_id, room_id, visibility } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/backup/add_backup_key_session.rs b/crates/ruma-client-api/src/r0/backup/add_backup_key_session.rs deleted file mode 100644 index e13bdd7c..00000000 --- a/crates/ruma-client-api/src/r0/backup/add_backup_key_session.rs +++ /dev/null @@ -1,74 +0,0 @@ -//! [PUT /_matrix/client/v3/room_keys/keys/{roomId}/{sessionId}](https://spec.matrix.org/v1.1/client-server-api/#put_matrixclientv3room_keyskeysroomidsessionid) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; -use ruma_serde::Raw; - -use super::KeyBackupData; - -ruma_api! { - metadata: { - description: "Store several keys in the backup.", - method: PUT, - name: "add_backup_key_session", - unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id/:session_id", - r0_path: "/_matrix/client/r0/room_keys/keys/:room_id/:session_id", - stable_path: "/_matrix/client/v3/room_keys/keys/:room_id/:session_id", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The backup version. - /// - /// Must be the current backup. - #[ruma_api(query)] - pub version: &'a str, - - /// The ID of the room that the requested key is for. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The ID of the megolm session whose key is requested. - #[ruma_api(path)] - pub session_id: &'a str, - - /// The key information to backup. - #[ruma_api(body)] - pub session_data: Raw, - } - - response: { - /// An opaque string representing stored keys in the backup. - /// - /// Clients can compare it with the etag value they received in the request of their last - /// key storage request. - pub etag: String, - - /// The number of keys stored in the backup. - pub count: UInt, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version, room_id, session_id and session_data. - pub fn new( - version: &'a str, - room_id: &'a RoomId, - session_id: &'a str, - session_data: Raw, - ) -> Self { - Self { version, room_id, session_id, session_data } - } -} - -impl Response { - /// Creates an new `Response` with the given etag and count. - pub fn new(etag: String, count: UInt) -> Self { - Self { etag, count } - } -} diff --git a/crates/ruma-client-api/src/r0/backup/add_backup_key_sessions.rs b/crates/ruma-client-api/src/r0/backup/add_backup_key_sessions.rs deleted file mode 100644 index 41e5bc7e..00000000 --- a/crates/ruma-client-api/src/r0/backup/add_backup_key_sessions.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! [PUT /_matrix/client/v3/room_keys/keys/{roomId}](https://spec.matrix.org/v1.1/client-server-api/#put_matrixclientv3room_keyskeysroomid) - -use std::collections::BTreeMap; - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; -use ruma_serde::Raw; - -use super::KeyBackupData; - -ruma_api! { - metadata: { - description: "Store several sessions in the backup.", - method: PUT, - name: "add_backup_key_sessions", - unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id", - r0_path: "/_matrix/client/r0/room_keys/keys/:room_id", - stable_path: "/_matrix/client/v3/room_keys/keys/:room_id", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The backup version. - /// - /// Must be the current backup. - #[ruma_api(query)] - pub version: &'a str, - - /// The ID of the room that the requested key is for. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// A map from session IDs to key data. - pub sessions: BTreeMap>, - } - - response: { - /// An opaque string representing stored keys in the backup. - /// - /// Clients can compare it with the etag value they received in the request of their last - /// key storage request. - pub etag: String, - - /// The number of keys stored in the backup. - pub count: UInt, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version, room_id and sessions. - pub fn new( - version: &'a str, - room_id: &'a RoomId, - sessions: BTreeMap>, - ) -> Self { - Self { version, room_id, sessions } - } -} - -impl Response { - /// Creates an new `Response` with the given etag and count. - pub fn new(etag: String, count: UInt) -> Self { - Self { etag, count } - } -} diff --git a/crates/ruma-client-api/src/r0/backup/add_backup_keys.rs b/crates/ruma-client-api/src/r0/backup/add_backup_keys.rs deleted file mode 100644 index 55a95282..00000000 --- a/crates/ruma-client-api/src/r0/backup/add_backup_keys.rs +++ /dev/null @@ -1,60 +0,0 @@ -//! [PUT /_matrix/client/v3/room_keys/keys](https://spec.matrix.org/v1.1/client-server-api/#put_matrixclientv3room_keyskeys) - -use std::collections::BTreeMap; - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -use super::RoomKeyBackup; - -ruma_api! { - metadata: { - description: "Store several keys in the backup.", - method: PUT, - name: "add_backup_keys", - unstable_path: "/_matrix/client/unstable/room_keys/keys", - stable_path: "/_matrix/client/v3/room_keys/keys", - rate_limited: true, - authentication: AccessToken, - added: 1.1, - } - - request: { - /// The backup version. - /// - /// Must be the current backup. - #[ruma_api(query)] - pub version: &'a str, - - /// A map from room IDs to session IDs to key data. - pub rooms: BTreeMap, RoomKeyBackup>, - } - - response: { - /// An opaque string representing stored keys in the backup. - /// - /// Clients can compare it with the etag value they received in the request of their last - /// key storage request. - pub etag: String, - - /// The number of keys stored in the backup. - pub count: UInt, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version and room key backups. - pub fn new(version: &'a str, rooms: BTreeMap, RoomKeyBackup>) -> Self { - Self { version, rooms } - } -} - -impl Response { - /// Creates a new `Response` with the given etag and key count. - pub fn new(etag: String, count: UInt) -> Self { - Self { etag, count } - } -} diff --git a/crates/ruma-client-api/src/r0/backup/create_backup.rs b/crates/ruma-client-api/src/r0/backup/create_backup.rs deleted file mode 100644 index df6a6f12..00000000 --- a/crates/ruma-client-api/src/r0/backup/create_backup.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! [POST /_matrix/client/v3/room_keys/version](https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3room_keysversion) - -use ruma_api::ruma_api; -use ruma_serde::Raw; - -use super::BackupAlgorithm; - -ruma_api! { - metadata: { - description: "Creates a new backup.", - method: POST, - name: "create_backup", - unstable_path: "/_matrix/client/unstable/room_keys/version", - stable_path: "/_matrix/client/v3/room_keys/version", - rate_limited: true, - authentication: AccessToken, - added: 1.1, - } - - request: { - /// The algorithm used for storing backups. - #[ruma_api(body)] - pub algorithm: Raw, - } - - response: { - /// The backup version. - pub version: String, - } - - error: crate::Error -} - -impl Request { - /// Creates a new `Request` with the given backup algorithm. - pub fn new(algorithm: Raw) -> Self { - Self { algorithm } - } -} - -impl Response { - /// Creates a new `Response` with the given version. - pub fn new(version: String) -> Self { - Self { version } - } -} diff --git a/crates/ruma-client-api/src/r0/backup/delete_backup.rs b/crates/ruma-client-api/src/r0/backup/delete_backup.rs deleted file mode 100644 index f46ef1f7..00000000 --- a/crates/ruma-client-api/src/r0/backup/delete_backup.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! [DELETE /_matrix/client/v3/room_keys/version/{version}](https://spec.matrix.org/v1.1/client-server-api/#delete_matrixclientv3room_keysversionversion) - -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "Delete an existing backup.", - method: DELETE, - name: "delete_backup", - unstable_path: "/_matrix/client/unstable/room_keys/version/:version", - r0_path: "/_matrix/client/r0/room_keys/version/:version", - stable_path: "/_matrix/client/v3/room_keys/version/:version", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The backup version. - #[ruma_api(path)] - pub version: &'a str, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version, room_id and sessions. - pub fn new(version: &'a str) -> Self { - Self { version } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/backup/delete_backup_key_session.rs b/crates/ruma-client-api/src/r0/backup/delete_backup_key_session.rs deleted file mode 100644 index 396ca59d..00000000 --- a/crates/ruma-client-api/src/r0/backup/delete_backup_key_session.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! [DELETE /_matrix/client/v3/room_keys/keys/{roomId}/{sessionId}](https://spec.matrix.org/v1.1/client-server-api/#delete_matrixclientv3room_keyskeysroomidsessionid) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -ruma_api! { - metadata: { - description: "Delete a key from the backup", - method: DELETE, - name: "delete_backup_key_session", - unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id/:session_id", - r0_path: "/_matrix/client/r0/room_keys/keys/:room_id/:session_id", - stable_path: "/_matrix/client/v3/room_keys/keys/:room_id/:session_id", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The backup version. - /// - /// Must be the current backup. - #[ruma_api(query)] - pub version: &'a str, - - /// The ID of the room that the requested key is for. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The ID of the megolm session whose key is requested. - #[ruma_api(path)] - pub session_id: &'a str, - } - - response: { - /// An opaque string representing stored keys in the backup. - /// - /// Clients can compare it with the etag value they received in the request of their last - /// key storage request. - pub etag: String, - - /// The number of keys stored in the backup. - pub count: UInt, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version, room_id and session_id. - pub fn new(version: &'a str, room_id: &'a RoomId, session_id: &'a str) -> Self { - Self { version, room_id, session_id } - } -} - -impl Response { - /// Creates an new `Response` with the given etag and count. - pub fn new(etag: String, count: UInt) -> Self { - Self { etag, count } - } -} diff --git a/crates/ruma-client-api/src/r0/backup/delete_backup_key_sessions.rs b/crates/ruma-client-api/src/r0/backup/delete_backup_key_sessions.rs deleted file mode 100644 index 1b08140c..00000000 --- a/crates/ruma-client-api/src/r0/backup/delete_backup_key_sessions.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! [DELETE /_matrix/client/v3/room_keys/keys/{roomId}](https://spec.matrix.org/v1.1/client-server-api/#delete_matrixclientv3room_keyskeysroomid) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -ruma_api! { - metadata: { - description: "Delete keys from the backup for a given room.", - method: DELETE, - name: "delete_backup_key_sessions", - unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id", - r0_path: "/_matrix/client/r0/room_keys/keys/:room_id", - stable_path: "/_matrix/client/v3/room_keys/keys/:room_id", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The backup version. - /// - /// Must be the current backup. - #[ruma_api(query)] - pub version: &'a str, - - /// The ID of the room that the requested key is for. - #[ruma_api(path)] - pub room_id: &'a RoomId, - } - - response: { - /// An opaque string representing stored keys in the backup. - /// - /// Clients can compare it with the etag value they received in the request of their last - /// key storage request. - pub etag: String, - - /// The number of keys stored in the backup. - pub count: UInt, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version and room_id. - - pub fn new(version: &'a str, room_id: &'a RoomId) -> Self { - Self { version, room_id } - } -} - -impl Response { - /// Creates an new `Response` with the given etag and count. - pub fn new(etag: String, count: UInt) -> Self { - Self { etag, count } - } -} diff --git a/crates/ruma-client-api/src/r0/backup/delete_backup_keys.rs b/crates/ruma-client-api/src/r0/backup/delete_backup_keys.rs deleted file mode 100644 index 96e50933..00000000 --- a/crates/ruma-client-api/src/r0/backup/delete_backup_keys.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! [DELETE /_matrix/client/v3/room_keys/keys](https://spec.matrix.org/v1.1/client-server-api/#delete_matrixclientv3room_keyskeys) - -use js_int::UInt; -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "Delete all keys in a backup.", - method: DELETE, - name: "delete_backup_keys", - unstable_path: "/_matrix/client/unstable/room_keys/keys", - r0_path: "/_matrix/client/r0/room_keys/keys", - stable_path: "/_matrix/client/v3/room_keys/keys", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The backup version. - /// - /// Must be the current backup. - #[ruma_api(query)] - pub version: &'a str, - } - - response: { - /// An opaque string representing stored keys in the backup. - /// - /// Clients can compare it with the etag value they received in the request of their last - /// key storage request. - pub etag: String, - - /// The number of keys stored in the backup. - pub count: UInt, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version. - pub fn new(version: &'a str) -> Self { - Self { version } - } -} - -impl Response { - /// Creates an new `Response` with the given etag and count. - pub fn new(etag: String, count: UInt) -> Self { - Self { etag, count } - } -} diff --git a/crates/ruma-client-api/src/r0/backup/get_backup.rs b/crates/ruma-client-api/src/r0/backup/get_backup.rs deleted file mode 100644 index 4b9d8cfb..00000000 --- a/crates/ruma-client-api/src/r0/backup/get_backup.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! [GET /_matrix/client/v3/room_keys/version/{version}](https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3room_keysversionversion) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_serde::Raw; -use serde::{ser, Deserialize, Deserializer, Serialize}; -use serde_json::value::{to_raw_value as to_raw_json_value, RawValue as RawJsonValue}; - -use super::BackupAlgorithm; - -ruma_api! { - metadata: { - description: "Get information about an existing backup.", - method: GET, - name: "get_backup", - unstable_path: "/_matrix/client/unstable/room_keys/version/:version", - stable_path: "/_matrix/client/v3/room_keys/version/:version", - rate_limited: true, - authentication: AccessToken, - added: 1.1, - } - - request: { - /// The backup version. - #[ruma_api(path)] - pub version: &'a str, - } - - #[ruma_api(manual_body_serde)] - response: { - /// The algorithm used for storing backups. - pub algorithm: Raw, - - /// The number of keys stored in the backup. - pub count: UInt, - - /// An opaque string representing stored keys in the backup. - /// - /// Clients can compare it with the etag value they received in the request of their last - /// key storage request. - pub etag: String, - - /// The backup version. - pub version: String, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version. - pub fn new(version: &'a str) -> Self { - Self { version } - } -} - -impl Response { - /// Creates a new `Response` with the gien algorithm, key count, etag and version. - pub fn new( - algorithm: Raw, - count: UInt, - etag: String, - version: String, - ) -> Self { - Self { algorithm, count, etag, version } - } -} - -#[derive(Deserialize)] -pub(super) struct ResponseBodyRepr { - pub algorithm: Box, - pub auth_data: Box, - pub count: UInt, - pub etag: String, - pub version: String, -} - -#[derive(Serialize)] -pub(super) struct RefResponseBodyRepr<'a> { - pub algorithm: &'a RawJsonValue, - pub auth_data: &'a RawJsonValue, - pub count: UInt, - pub etag: &'a str, - pub version: &'a str, -} - -#[derive(Deserialize, Serialize)] -pub(super) struct AlgorithmWithData { - pub algorithm: Box, - pub auth_data: Box, -} - -impl<'de> Deserialize<'de> for ResponseBody { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let ResponseBodyRepr { algorithm, auth_data, count, etag, version } = - ResponseBodyRepr::deserialize(deserializer)?; - - let algorithm = - Raw::from_json(to_raw_json_value(&AlgorithmWithData { algorithm, auth_data }).unwrap()); - - Ok(Self { algorithm, count, etag, version }) - } -} - -impl Serialize for ResponseBody { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let ResponseBody { algorithm, count, etag, version } = self; - let AlgorithmWithData { algorithm, auth_data } = - algorithm.deserialize_as().map_err(ser::Error::custom)?; - - let repr = RefResponseBodyRepr { - algorithm: &algorithm, - auth_data: &auth_data, - count: *count, - etag, - version, - }; - - repr.serialize(serializer) - } -} diff --git a/crates/ruma-client-api/src/r0/backup/get_backup_key_session.rs b/crates/ruma-client-api/src/r0/backup/get_backup_key_session.rs deleted file mode 100644 index b1c26b23..00000000 --- a/crates/ruma-client-api/src/r0/backup/get_backup_key_session.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! [GET /_matrix/client/v3/room_keys/keys/{roomId}/{sessionId}](https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3room_keyskeysroomidsessionid) - -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; -use ruma_serde::Raw; - -use super::KeyBackupData; - -ruma_api! { - metadata: { - description: "Retrieve a key from the backup", - method: GET, - name: "get_backup_key_session", - unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id/:session_id", - r0_path: "/_matrix/client/r0/room_keys/keys/:room_id/:session_id", - stable_path: "/_matrix/client/v3/room_keys/keys/:room_id/:session_id", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The backup version. - /// - /// Must be the current backup. - #[ruma_api(query)] - pub version: &'a str, - - /// The ID of the room that the requested key is for. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The ID of the megolm session whose key is requested. - #[ruma_api(path)] - pub session_id: &'a str, - } - - response: { - /// Information about the requested backup key. - #[ruma_api(body)] - pub key_data: Raw, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version, room_id and session_id. - pub fn new(version: &'a str, room_id: &'a RoomId, session_id: &'a str) -> Self { - Self { version, room_id, session_id } - } -} - -impl Response { - /// Creates a new `Response` with the given key_data. - pub fn new(key_data: Raw) -> Self { - Self { key_data } - } -} diff --git a/crates/ruma-client-api/src/r0/backup/get_backup_key_sessions.rs b/crates/ruma-client-api/src/r0/backup/get_backup_key_sessions.rs deleted file mode 100644 index cc3ff37a..00000000 --- a/crates/ruma-client-api/src/r0/backup/get_backup_key_sessions.rs +++ /dev/null @@ -1,56 +0,0 @@ -//! [GET /_matrix/client/v3/room_keys/keys/{roomId}](https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3room_keyskeysroomid) - -use std::collections::BTreeMap; - -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; -use ruma_serde::Raw; - -use super::KeyBackupData; - -ruma_api! { - metadata: { - description: "Retrieve sessions from the backup for a given room.", - method: GET, - name: "get_backup_key_sessions", - unstable_path: "/_matrix/client/unstable/room_keys/keys/:room_id", - r0_path: "/_matrix/client/r0/room_keys/keys/:room_id", - stable_path: "/_matrix/client/v3/room_keys/keys/:room_id", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The backup version - /// - /// Must be the current backup. - #[ruma_api(query)] - pub version: &'a str, - - /// The ID of the room that the requested key is for. - #[ruma_api(path)] - pub room_id: &'a RoomId, - } - - response: { - /// A map of session IDs to key data. - pub sessions: BTreeMap>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version and room_id. - pub fn new(version: &'a str, room_id: &'a RoomId) -> Self { - Self { version, room_id } - } -} - -impl Response { - /// Creates a new `Response` with the given sessions. - pub fn new(sessions: BTreeMap>) -> Self { - Self { sessions } - } -} diff --git a/crates/ruma-client-api/src/r0/backup/get_backup_keys.rs b/crates/ruma-client-api/src/r0/backup/get_backup_keys.rs deleted file mode 100644 index 3c3023f5..00000000 --- a/crates/ruma-client-api/src/r0/backup/get_backup_keys.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! [GET /_matrix/client/v3/room_keys/keys](https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3room_keyskeys) - -use std::collections::BTreeMap; - -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -use super::RoomKeyBackup; - -ruma_api! { - metadata: { - description: "Retrieve all keys from a backup.", - method: GET, - name: "get_backup_keys", - unstable_path: "/_matrix/client/unstable/room_keys/keys", - r0_path: "/_matrix/client/r0/room_keys/keys", - stable_path: "/_matrix/client/v3/room_keys/keys", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The backup version. - /// - /// Must be the current backup. - #[ruma_api(query)] - pub version: &'a str, - } - - response: { - /// A map from room IDs to session IDs to key data. - pub rooms: BTreeMap, RoomKeyBackup>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given version. - pub fn new(version: &'a str) -> Self { - Self { version } - } -} - -impl Response { - /// Creates a new `Response` with the given room key backups. - pub fn new(rooms: BTreeMap, RoomKeyBackup>) -> Self { - Self { rooms } - } -} diff --git a/crates/ruma-client-api/src/r0/backup/get_latest_backup.rs b/crates/ruma-client-api/src/r0/backup/get_latest_backup.rs deleted file mode 100644 index 7f387895..00000000 --- a/crates/ruma-client-api/src/r0/backup/get_latest_backup.rs +++ /dev/null @@ -1,104 +0,0 @@ -//! [GET /_matrix/client/v3/room_keys/version](https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3room_keysversion) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_serde::Raw; -use serde::{ser, Deserialize, Deserializer, Serialize}; -use serde_json::value::to_raw_value as to_raw_json_value; - -use super::{ - get_backup::{AlgorithmWithData, RefResponseBodyRepr, ResponseBodyRepr}, - BackupAlgorithm, -}; - -ruma_api! { - metadata: { - description: "Get information about the latest backup.", - method: GET, - name: "get_latest_backup", - unstable_path: "/_matrix/client/unstable/room_keys/version", - r0_path: "/_matrix/client/r0/room_keys/version", - stable_path: "/_matrix/client/v3/room_keys/version", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - #[ruma_api(manual_body_serde)] - response: { - /// The algorithm used for storing backups. - pub algorithm: Raw, - - /// The number of keys stored in the backup. - pub count: UInt, - - /// An opaque string representing stored keys in the backup. - /// - /// Clients can compare it with the etag value they received in the request of their last - /// key storage request. - pub etag: String, - - /// The backup version. - pub version: String, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given algorithm, key count, etag and version. - pub fn new( - algorithm: Raw, - count: UInt, - etag: String, - version: String, - ) -> Self { - Self { algorithm, count, etag, version } - } -} - -impl<'de> Deserialize<'de> for ResponseBody { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let ResponseBodyRepr { algorithm, auth_data, count, etag, version } = - ResponseBodyRepr::deserialize(deserializer)?; - - let algorithm = - Raw::from_json(to_raw_json_value(&AlgorithmWithData { algorithm, auth_data }).unwrap()); - - Ok(Self { algorithm, count, etag, version }) - } -} - -impl Serialize for ResponseBody { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let ResponseBody { algorithm, count, etag, version } = self; - let AlgorithmWithData { algorithm, auth_data } = - algorithm.deserialize_as().map_err(ser::Error::custom)?; - - let repr = RefResponseBodyRepr { - algorithm: &algorithm, - auth_data: &auth_data, - count: *count, - etag, - version, - }; - - repr.serialize(serializer) - } -} diff --git a/crates/ruma-client-api/src/r0/backup/update_backup.rs b/crates/ruma-client-api/src/r0/backup/update_backup.rs deleted file mode 100644 index 652fe62b..00000000 --- a/crates/ruma-client-api/src/r0/backup/update_backup.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! [POST /_matrix/client/v3/room_keys/version](https://spec.matrix.org/v1.1/client-server-api/#post_matrixclientv3room_keysversion) - -use ruma_api::ruma_api; -use ruma_serde::Raw; - -use super::BackupAlgorithm; - -ruma_api! { - metadata: { - description: "Update information about an existing backup.", - method: POST, - name: "update_backup", - unstable_path: "/_matrix/client/unstable/room_keys/version/:version", - stable_path: "/_matrix/client/v3/room_keys/version/:version", - rate_limited: true, - authentication: AccessToken, - added: 1.1, - } - - request: { - /// The backup version. - #[ruma_api(path)] - pub version: &'a str, - - /// The algorithm used for storing backups. - #[ruma_api(body)] - pub algorithm: Raw, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given backup version and algorithm. - pub fn new(version: &'a str, algorithm: Raw) -> Self { - Self { version, algorithm } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/capabilities/get_capabilities.rs b/crates/ruma-client-api/src/r0/capabilities/get_capabilities.rs deleted file mode 100644 index b3adef92..00000000 --- a/crates/ruma-client-api/src/r0/capabilities/get_capabilities.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! [GET /_matrix/client/r0/capabilities](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-capabilities) - -use ruma_api::ruma_api; - -use super::Capabilities; - -ruma_api! { - metadata: { - description: "Gets information about the server's supported feature set and other relevant capabilities.", - method: GET, - name: "get_capabilities", - r0_path: "/_matrix/client/r0/capabilities", - stable_path: "/_matrix/client/v3/capabilities", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// The capabilities the server supports - pub capabilities: Capabilities, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given capabilities. - pub fn new(capabilities: Capabilities) -> Self { - Self { capabilities } - } -} - -impl From for Response { - fn from(capabilities: Capabilities) -> Self { - Self::new(capabilities) - } -} diff --git a/crates/ruma-client-api/src/r0/config/get_global_account_data.rs b/crates/ruma-client-api/src/r0/config/get_global_account_data.rs deleted file mode 100644 index 664479db..00000000 --- a/crates/ruma-client-api/src/r0/config/get_global_account_data.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! [GET /_matrix/client/r0/user/{userId}/account_data/{type}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-user-userid-account-data-type) - -use ruma_api::ruma_api; -use ruma_events::AnyGlobalAccountDataEventContent; -use ruma_identifiers::UserId; -use ruma_serde::Raw; - -ruma_api! { - metadata: { - description: "Gets global account data for a user.", - name: "get_global_account_data", - method: GET, - r0_path: "/_matrix/client/r0/user/:user_id/account_data/:event_type", - stable_path: "/_matrix/client/v3/user/:user_id/account_data/:event_type", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// User ID of user for whom to retrieve data. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// Type of data to retrieve. - #[ruma_api(path)] - pub event_type: &'a str, - } - - response: { - /// Account data content for the given type. - /// - /// Use `ruma_events::RawExt` for deserialization. - #[ruma_api(body)] - pub account_data: Raw, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID and event type. - pub fn new(user_id: &'a UserId, event_type: &'a str) -> Self { - Self { user_id, event_type } - } -} - -impl Response { - /// Creates a new `Response` with the given account data. - pub fn new(account_data: Raw) -> Self { - Self { account_data } - } -} diff --git a/crates/ruma-client-api/src/r0/config/get_room_account_data.rs b/crates/ruma-client-api/src/r0/config/get_room_account_data.rs deleted file mode 100644 index ba6f5dd7..00000000 --- a/crates/ruma-client-api/src/r0/config/get_room_account_data.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! [GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-user-userid-rooms-roomid-account-data-type) - -use ruma_api::ruma_api; -use ruma_events::AnyRoomAccountDataEventContent; -use ruma_identifiers::{RoomId, UserId}; -use ruma_serde::Raw; - -ruma_api! { - metadata: { - description: "Gets account data room for a user for a given room", - name: "get_room_account_data", - method: GET, - r0_path: "/_matrix/client/r0/user/:user_id/rooms/:room_id/account_data/:event_type", - stable_path: "/_matrix/client/v3/user/:user_id/rooms/:room_id/account_data/:event_type", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// User ID of user for whom to retrieve data. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// Room ID for which to retrieve data. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// Type of data to retrieve. - #[ruma_api(path)] - pub event_type: &'a str, - } - - response: { - /// Account data content for the given type. - /// - /// Use `ruma_events::RawExt` for deserialization. - #[ruma_api(body)] - pub account_data: Raw, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID, room ID and event type. - pub fn new(user_id: &'a UserId, room_id: &'a RoomId, event_type: &'a str) -> Self { - Self { user_id, room_id, event_type } - } -} - -impl Response { - /// Creates a new `Response` with the given account data. - pub fn new(account_data: Raw) -> Self { - Self { account_data } - } -} diff --git a/crates/ruma-client-api/src/r0/config/set_global_account_data.rs b/crates/ruma-client-api/src/r0/config/set_global_account_data.rs deleted file mode 100644 index 362a96e2..00000000 --- a/crates/ruma-client-api/src/r0/config/set_global_account_data.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! [PUT /_matrix/client/r0/user/{userId}/account_data/{type}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-user-userid-account-data-type) - -use ruma_api::ruma_api; -use ruma_identifiers::UserId; -use serde_json::value::RawValue as RawJsonValue; - -ruma_api! { - metadata: { - description: "Sets global account data.", - method: PUT, - name: "set_global_account_data", - r0_path: "/_matrix/client/r0/user/:user_id/account_data/:event_type", - stable_path: "/_matrix/client/v3/user/:user_id/account_data/:event_type", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The ID of the user to set account_data for. - /// - /// The access token must be authorized to make requests for this user ID. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// The event type of the account_data to set. - /// - /// Custom types should be namespaced to avoid clashes. - #[ruma_api(path)] - pub event_type: &'a str, - - /// Arbitrary JSON to store as config data. - /// - /// To create a `RawJsonValue`, use `serde_json::value::to_raw_value`. - #[ruma_api(body)] - pub data: &'a RawJsonValue, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given data, event type and user ID. - pub fn new(data: &'a RawJsonValue, event_type: &'a str, user_id: &'a UserId) -> Self { - Self { user_id, event_type, data } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/config/set_room_account_data.rs b/crates/ruma-client-api/src/r0/config/set_room_account_data.rs deleted file mode 100644 index 02c3dad0..00000000 --- a/crates/ruma-client-api/src/r0/config/set_room_account_data.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! [PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/account_data/{type}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-user-userid-rooms-roomid-account-data-type) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomId, UserId}; -use serde_json::value::RawValue as RawJsonValue; - -ruma_api! { - metadata: { - description: "Associate account data with a room.", - method: PUT, - name: "set_room_account_data", - r0_path: "/_matrix/client/r0/user/:user_id/rooms/:room_id/account_data/:event_type", - stable_path: "/_matrix/client/v3/user/:user_id/rooms/:room_id/account_data/:event_type", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// Arbitrary JSON to store as config data. - /// - /// To create a `RawJsonValue`, use `serde_json::value::to_raw_value`. - #[ruma_api(body)] - pub data: &'a RawJsonValue, - - /// The event type of the account_data to set. - /// - /// Custom types should be namespaced to avoid clashes. - #[ruma_api(path)] - pub event_type: &'a str, - - /// The ID of the room to set account_data on. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The ID of the user to set account_data for. - /// - /// The access token must be authorized to make requests for this user ID. - #[ruma_api(path)] - pub user_id: &'a UserId, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given data, event type, room ID and user ID. - pub fn new( - data: &'a RawJsonValue, - event_type: &'a str, - room_id: &'a RoomId, - user_id: &'a UserId, - ) -> Self { - Self { data, event_type, room_id, user_id } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/context/get_context.rs b/crates/ruma-client-api/src/r0/context/get_context.rs deleted file mode 100644 index 92843f14..00000000 --- a/crates/ruma-client-api/src/r0/context/get_context.rs +++ /dev/null @@ -1,102 +0,0 @@ -//! [GET /_matrix/client/r0/rooms/{roomId}/context/{eventId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-context-eventid) - -use js_int::{uint, UInt}; -use ruma_api::ruma_api; -use ruma_events::{AnyRoomEvent, AnyStateEvent}; -use ruma_identifiers::{EventId, RoomId}; -use ruma_serde::Raw; - -use crate::r0::filter::{IncomingRoomEventFilter, RoomEventFilter}; - -ruma_api! { - metadata: { - description: "Get the events immediately preceding and following a given event.", - method: GET, - r0_path: "/_matrix/client/r0/rooms/:room_id/context/:event_id", - stable_path: "/_matrix/client/v3/rooms/:room_id/context/:event_id", - name: "get_context", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to get events from. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The event to get context around. - #[ruma_api(path)] - pub event_id: &'a EventId, - - /// The maximum number of events to return. - /// - /// Defaults to 10. - #[ruma_api(query)] - #[serde(default = "default_limit", skip_serializing_if = "is_default_limit")] - pub limit: UInt, - - /// A RoomEventFilter to filter returned events with. - #[ruma_api(query)] - #[serde( - with = "ruma_serde::json_string", - default, - skip_serializing_if = "RoomEventFilter::is_empty" - )] - pub filter: RoomEventFilter<'a>, - } - - #[derive(Default)] - response: { - /// A token that can be used to paginate backwards with. - #[serde(skip_serializing_if = "Option::is_none")] - pub start: Option, - - /// A token that can be used to paginate forwards with. - #[serde(skip_serializing_if = "Option::is_none")] - pub end: Option, - - /// A list of room events that happened just before the requested event, - /// in reverse-chronological order. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events_before: Vec>, - - /// Details of the requested event. - #[serde(skip_serializing_if = "Option::is_none")] - pub event: Option>, - - /// A list of room events that happened just after the requested event, - /// in chronological order. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events_after: Vec>, - - /// The state of the room at the last event returned. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub state: Vec>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room id and event id. - pub fn new(room_id: &'a RoomId, event_id: &'a EventId) -> Self { - Self { room_id, event_id, limit: default_limit(), filter: RoomEventFilter::default() } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Default::default() - } -} - -fn default_limit() -> UInt { - uint!(10) -} - -#[allow(clippy::trivially_copy_pass_by_ref)] -fn is_default_limit(val: &UInt) -> bool { - *val == default_limit() -} diff --git a/crates/ruma-client-api/src/r0/device/delete_device.rs b/crates/ruma-client-api/src/r0/device/delete_device.rs deleted file mode 100644 index ae847630..00000000 --- a/crates/ruma-client-api/src/r0/device/delete_device.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! [DELETE /_matrix/client/r0/devices/{deviceId}](https://matrix.org/docs/spec/client_server/r0.6.1#delete-matrix-client-r0-devices-deviceid) - -use ruma_api::ruma_api; -use ruma_identifiers::DeviceId; - -use crate::r0::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; - -ruma_api! { - metadata: { - description: "Delete a device for authenticated user.", - method: DELETE, - name: "delete_device", - r0_path: "/_matrix/client/r0/devices/:device_id", - stable_path: "/_matrix/client/v3/devices/:device_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The device to delete. - #[ruma_api(path)] - pub device_id: &'a DeviceId, - - /// Additional authentication information for the user-interactive authentication API. - #[serde(skip_serializing_if = "Option::is_none")] - pub auth: Option>, - } - - #[derive(Default)] - response: {} - - error: UiaaResponse -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given device ID. - pub fn new(device_id: &'a DeviceId) -> Self { - Self { device_id, auth: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/device/delete_devices.rs b/crates/ruma-client-api/src/r0/device/delete_devices.rs deleted file mode 100644 index ec0f62de..00000000 --- a/crates/ruma-client-api/src/r0/device/delete_devices.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! [POST /_matrix/client/r0/delete_devices](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-delete-devices) - -use ruma_api::ruma_api; -use ruma_identifiers::DeviceId; - -use crate::r0::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; - -ruma_api! { - metadata: { - description: "Delete specified devices.", - method: POST, - r0_path: "/_matrix/client/r0/delete_devices", - stable_path: "/_matrix/client/v3/delete_devices", - name: "delete_devices", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// List of devices to delete. - pub devices: &'a [Box], - - /// Additional authentication information for the user-interactive authentication API. - #[serde(skip_serializing_if = "Option::is_none")] - pub auth: Option>, - } - - #[derive(Default)] - response: {} - - error: UiaaResponse -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given device list. - pub fn new(devices: &'a [Box]) -> Self { - Self { devices, auth: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/device/get_device.rs b/crates/ruma-client-api/src/r0/device/get_device.rs deleted file mode 100644 index 50bd4e17..00000000 --- a/crates/ruma-client-api/src/r0/device/get_device.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! [GET /_matrix/client/r0/devices/{deviceId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-devices-deviceid) - -use ruma_api::ruma_api; -use ruma_identifiers::DeviceId; - -use super::Device; - -ruma_api! { - metadata: { - description: "Get a device for authenticated user.", - method: GET, - name: "get_device", - r0_path: "/_matrix/client/r0/devices/:device_id", - stable_path: "/_matrix/client/v3/devices/:device_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The device to retrieve. - #[ruma_api(path)] - pub device_id: &'a DeviceId, - } - - response: { - /// Information about the device. - #[ruma_api(body)] - pub device: Device, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given device ID. - pub fn new(device_id: &'a DeviceId) -> Self { - Self { device_id } - } -} - -impl Response { - /// Creates a new `Response` with the given device. - pub fn new(device: Device) -> Self { - Self { device } - } -} diff --git a/crates/ruma-client-api/src/r0/device/get_devices.rs b/crates/ruma-client-api/src/r0/device/get_devices.rs deleted file mode 100644 index 0a6b5184..00000000 --- a/crates/ruma-client-api/src/r0/device/get_devices.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! [GET /_matrix/client/r0/devices](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-devices) - -use ruma_api::ruma_api; - -use super::Device; - -ruma_api! { - metadata: { - description: "Get registered devices for authenticated user.", - method: GET, - name: "get_devices", - r0_path: "/_matrix/client/r0/devices", - stable_path: "/_matrix/client/v3/devices", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// A list of all registered devices for this user - pub devices: Vec, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given devices. - pub fn new(devices: Vec) -> Self { - Self { devices } - } -} diff --git a/crates/ruma-client-api/src/r0/device/update_device.rs b/crates/ruma-client-api/src/r0/device/update_device.rs deleted file mode 100644 index 4dfa29df..00000000 --- a/crates/ruma-client-api/src/r0/device/update_device.rs +++ /dev/null @@ -1,48 +0,0 @@ -//! [PUT /_matrix/client/r0/devices/{deviceId}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-devices-deviceid) - -use ruma_api::ruma_api; -use ruma_identifiers::DeviceId; - -ruma_api! { - metadata: { - description: "Update metadata for a device.", - method: PUT, - name: "update_device", - r0_path: "/_matrix/client/r0/devices/:device_id", - stable_path: "/_matrix/client/v3/devices/:device_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The device to update. - #[ruma_api(path)] - pub device_id: &'a DeviceId, - - /// The new display name for this device. - /// - /// If this is `None`, the display name won't be changed. - #[serde(skip_serializing_if = "Option::is_none")] - pub display_name: Option, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given device ID. - pub fn new(device_id: &'a DeviceId) -> Self { - Self { device_id, display_name: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/directory/get_public_rooms.rs b/crates/ruma-client-api/src/r0/directory/get_public_rooms.rs deleted file mode 100644 index 0a188986..00000000 --- a/crates/ruma-client-api/src/r0/directory/get_public_rooms.rs +++ /dev/null @@ -1,124 +0,0 @@ -//! [GET /_matrix/client/r0/publicRooms](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-publicrooms) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_common::directory::PublicRoomsChunk; -use ruma_identifiers::ServerName; - -ruma_api! { - metadata: { - description: "Get the list of rooms in this homeserver's public directory.", - method: GET, - name: "get_public_rooms", - r0_path: "/_matrix/client/r0/publicRooms", - stable_path: "/_matrix/client/v3/publicRooms", - rate_limited: false, - authentication: None, - added: 1.0, - } - - #[derive(Default)] - request: { - /// Limit for the number of results to return. - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub limit: Option, - - /// Pagination token from a previous request. - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub since: Option<&'a str>, - - /// The server to fetch the public room lists from. - /// - /// `None` means the server this request is sent to. - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub server: Option<&'a ServerName>, - } - - response: { - /// A paginated chunk of public rooms. - pub chunk: Vec, - - /// A pagination token for the response. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_batch: Option, - - /// A pagination token that allows fetching previous results. - #[serde(skip_serializing_if = "Option::is_none")] - pub prev_batch: Option, - - /// An estimate on the total number of public rooms, if the server has an estimate. - #[serde(skip_serializing_if = "Option::is_none")] - pub total_room_count_estimate: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates an empty `Request`. - pub fn new() -> Self { - Default::default() - } -} - -impl Response { - /// Creates a new `Response` with the given room list chunk. - pub fn new(chunk: Vec) -> Self { - Self { chunk, next_batch: None, prev_batch: None, total_room_count_estimate: None } - } -} - -#[cfg(all(test, any(feature = "client", feature = "server")))] -mod tests { - use js_int::uint; - - #[cfg(feature = "client")] - #[test] - fn construct_request_from_refs() { - use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken}; - use ruma_identifiers::server_name; - - let req = super::Request { - limit: Some(uint!(10)), - since: Some("hello"), - server: Some(server_name!("test.tld")), - } - .try_into_http_request::>( - "https://homeserver.tld", - SendAccessToken::IfRequired("auth_tok"), - &[MatrixVersion::V1_1], - ) - .unwrap(); - - let uri = req.uri(); - let query = uri.query().unwrap(); - - assert_eq!(uri.path(), "/_matrix/client/v3/publicRooms"); - assert!(query.contains("since=hello")); - assert!(query.contains("limit=10")); - assert!(query.contains("server=test.tld")); - } - - #[cfg(feature = "server")] - #[test] - fn construct_response_from_refs() { - use ruma_api::OutgoingResponse as _; - - let res = super::Response { - chunk: vec![], - next_batch: Some("next_batch_token".into()), - prev_batch: Some("prev_batch_token".into()), - total_room_count_estimate: Some(uint!(10)), - } - .try_into_http_response::>() - .unwrap(); - - assert_eq!( - String::from_utf8_lossy(res.body()), - r#"{"chunk":[],"next_batch":"next_batch_token","prev_batch":"prev_batch_token","total_room_count_estimate":10}"# - ); - } -} diff --git a/crates/ruma-client-api/src/r0/directory/get_public_rooms_filtered.rs b/crates/ruma-client-api/src/r0/directory/get_public_rooms_filtered.rs deleted file mode 100644 index 9d6b0268..00000000 --- a/crates/ruma-client-api/src/r0/directory/get_public_rooms_filtered.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! [POST /_matrix/client/r0/publicRooms](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-publicrooms) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_common::directory::{ - Filter, IncomingFilter, IncomingRoomNetwork, PublicRoomsChunk, RoomNetwork, -}; -use ruma_identifiers::ServerName; - -ruma_api! { - metadata: { - description: "Get the list of rooms in this homeserver's public directory.", - method: POST, - name: "get_public_rooms_filtered", - r0_path: "/_matrix/client/r0/publicRooms", - stable_path: "/_matrix/client/v3/publicRooms", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: { - /// The server to fetch the public room lists from. - /// - /// `None` means the server this request is sent to. - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub server: Option<&'a ServerName>, - - /// Limit for the number of results to return. - #[serde(skip_serializing_if = "Option::is_none")] - pub limit: Option, - - /// Pagination token from a previous request. - #[serde(skip_serializing_if = "Option::is_none")] - pub since: Option<&'a str>, - - /// Filter to apply to the results. - #[serde(default, skip_serializing_if = "Filter::is_empty")] - pub filter: Filter<'a>, - - /// Network to fetch the public room lists from. - #[serde(flatten, skip_serializing_if = "ruma_serde::is_default")] - pub room_network: RoomNetwork<'a>, - } - - #[derive(Default)] - response: { - /// A paginated chunk of public rooms. - pub chunk: Vec, - - /// A pagination token for the response. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_batch: Option, - - /// A pagination token that allows fetching previous results. - #[serde(skip_serializing_if = "Option::is_none")] - pub prev_batch: Option, - - /// An estimate on the total number of public rooms, if the server has an estimate. - #[serde(skip_serializing_if = "Option::is_none")] - pub total_room_count_estimate: Option, - } - - error: crate::Error -} - -impl Request<'_> { - /// Creates an empty `Request`. - pub fn new() -> Self { - Default::default() - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Default::default() - } -} diff --git a/crates/ruma-client-api/src/r0/directory/get_room_visibility.rs b/crates/ruma-client-api/src/r0/directory/get_room_visibility.rs deleted file mode 100644 index 1c8909c2..00000000 --- a/crates/ruma-client-api/src/r0/directory/get_room_visibility.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! [GET /_matrix/client/r0/directory/list/room/{roomId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-directory-list-room-roomid) - -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -use crate::r0::room::Visibility; - -ruma_api! { - metadata: { - description: "Get the visibility of a public room on a directory.", - name: "get_room_visibility", - method: GET, - r0_path: "/_matrix/client/r0/directory/list/room/:room_id", - stable_path: "/_matrix/client/v3/directory/list/room/:room_id", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// The ID of the room of which to request the visibility. - #[ruma_api(path)] - pub room_id: &'a RoomId, - } - - response: { - /// Visibility of the room. - pub visibility: Visibility, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID. - pub fn new(room_id: &'a RoomId) -> Self { - Self { room_id } - } -} - -impl Response { - /// Creates a new `Response` with the given visibility. - pub fn new(visibility: Visibility) -> Self { - Self { visibility } - } -} diff --git a/crates/ruma-client-api/src/r0/directory/set_room_visibility.rs b/crates/ruma-client-api/src/r0/directory/set_room_visibility.rs deleted file mode 100644 index 274f712d..00000000 --- a/crates/ruma-client-api/src/r0/directory/set_room_visibility.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! [PUT /_matrix/client/r0/directory/list/room/{roomId}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-directory-list-room-roomid) - -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -use crate::r0::room::Visibility; - -ruma_api! { - metadata: { - description: "Set the visibility of a public room on a directory.", - name: "set_room_visibility", - method: PUT, - r0_path: "/_matrix/client/r0/directory/list/room/:room_id", - stable_path: "/_matrix/client/v3/directory/list/room/:room_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The ID of the room of which to set the visibility. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// New visibility setting for the room. - pub visibility: Visibility, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID and visibility. - pub fn new(room_id: &'a RoomId, visibility: Visibility) -> Self { - Self { room_id, visibility } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/filter/create_filter.rs b/crates/ruma-client-api/src/r0/filter/create_filter.rs deleted file mode 100644 index f367af91..00000000 --- a/crates/ruma-client-api/src/r0/filter/create_filter.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! [POST /_matrix/client/r0/user/{userId}/filter](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-user-userid-filter) - -use ruma_api::ruma_api; -use ruma_identifiers::UserId; - -use super::{FilterDefinition, IncomingFilterDefinition}; - -ruma_api! { - metadata: { - description: "Create a new filter for event retrieval.", - method: POST, - name: "create_filter", - r0_path: "/_matrix/client/r0/user/:user_id/filter", - stable_path: "/_matrix/client/v3/user/:user_id/filter", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The ID of the user uploading the filter. - /// - /// The access token must be authorized to make requests for this user ID. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// The filter definition. - #[ruma_api(body)] - pub filter: FilterDefinition<'a>, - } - - response: { - /// The ID of the filter that was created. - pub filter_id: String, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID and filter definition. - pub fn new(user_id: &'a UserId, filter: FilterDefinition<'a>) -> Self { - Self { user_id, filter } - } -} - -impl Response { - /// Creates a new `Response` with the given filter ID. - pub fn new(filter_id: String) -> Self { - Self { filter_id } - } -} - -#[cfg(all(test, any(feature = "client", feature = "server")))] -mod tests { - use matches::assert_matches; - - #[cfg(feature = "server")] - #[test] - fn deserialize_request() { - use ruma_api::IncomingRequest as _; - - use super::IncomingRequest; - - assert_matches!( - IncomingRequest::try_from_http_request( - http::Request::builder() - .method(http::Method::POST) - .uri("https://matrix.org/_matrix/client/r0/user/@foo:bar.com/filter") - .body(b"{}" as &[u8]) - .unwrap(), - &["@foo:bar.com"] - ), - Ok(IncomingRequest { user_id, filter }) - if user_id == "@foo:bar.com" && filter.is_empty() - ); - } - - #[cfg(feature = "client")] - #[test] - fn serialize_request() { - use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken}; - use ruma_identifiers::user_id; - - use crate::r0::filter::FilterDefinition; - - assert_matches!( - super::Request::new(user_id!("@foo:bar.com"), FilterDefinition::default()) - .try_into_http_request::>( - "https://matrix.org", - SendAccessToken::IfRequired("tok"), - &[MatrixVersion::V1_1] - ), - Ok(res) if res.body() == b"{}" - ); - } -} diff --git a/crates/ruma-client-api/src/r0/filter/get_filter.rs b/crates/ruma-client-api/src/r0/filter/get_filter.rs deleted file mode 100644 index f4f16b03..00000000 --- a/crates/ruma-client-api/src/r0/filter/get_filter.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! [GET /_matrix/client/r0/user/{userId}/filter/{filterId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-user-userid-filter-filterid) - -use ruma_api::ruma_api; -use ruma_identifiers::UserId; - -use super::IncomingFilterDefinition; - -ruma_api! { - metadata: { - description: "Retrieve a previously created filter.", - method: GET, - name: "get_filter", - r0_path: "/_matrix/client/r0/user/:user_id/filter/:filter_id", - stable_path: "/_matrix/client/v3/user/:user_id/filter/:filter_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The user ID to download a filter for. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// The ID of the filter to download. - #[ruma_api(path)] - pub filter_id: &'a str, - } - - response: { - /// The filter definition. - #[ruma_api(body)] - pub filter: IncomingFilterDefinition, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID and filter ID. - pub fn new(user_id: &'a UserId, filter_id: &'a str) -> Self { - Self { user_id, filter_id } - } -} - -impl Response { - /// Creates a new `Response` with the given filter definition. - pub fn new(filter: IncomingFilterDefinition) -> Self { - Self { filter } - } -} - -#[cfg(all(test, any(feature = "client", feature = "server")))] -mod tests { - use matches::assert_matches; - - #[cfg(feature = "client")] - #[test] - fn deserialize_response() { - use ruma_api::IncomingResponse; - - assert_matches!( - super::Response::try_from_http_response( - http::Response::builder().body(b"{}" as &[u8]).unwrap(), - ), - Ok(super::Response { filter }) if filter.is_empty() - ); - } - - #[cfg(feature = "server")] - #[test] - fn serialize_response() { - use ruma_api::OutgoingResponse; - - use crate::r0::filter::IncomingFilterDefinition; - - assert_matches!( - super::Response::new(IncomingFilterDefinition::default()) - .try_into_http_response::>(), - Ok(res) if res.body() == b"{}" - ); - } -} diff --git a/crates/ruma-client-api/src/r0/keys/claim_keys.rs b/crates/ruma-client-api/src/r0/keys/claim_keys.rs deleted file mode 100644 index 5c1845a7..00000000 --- a/crates/ruma-client-api/src/r0/keys/claim_keys.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! [POST /_matrix/client/r0/keys/claim](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-keys-claim) - -use std::{collections::BTreeMap, time::Duration}; - -use ruma_api::ruma_api; -use ruma_common::encryption::OneTimeKey; -use ruma_identifiers::{DeviceId, DeviceKeyAlgorithm, DeviceKeyId, UserId}; -use ruma_serde::Raw; -use serde_json::Value as JsonValue; - -ruma_api! { - metadata: { - description: "Claims one-time keys for use in pre-key messages.", - method: POST, - name: "claim_keys", - r0_path: "/_matrix/client/r0/keys/claim", - stable_path: "/_matrix/client/v3/keys/claim", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The time (in milliseconds) to wait when downloading keys from remote servers. - /// 10 seconds is the recommended default. - #[serde( - with = "ruma_serde::duration::opt_ms", - default, - skip_serializing_if = "Option::is_none", - )] - pub timeout: Option, - - /// The keys to be claimed. - pub one_time_keys: BTreeMap, BTreeMap, DeviceKeyAlgorithm>>, - } - - response: { - /// If any remote homeservers could not be reached, they are recorded here. - /// The names of the properties are the names of the unreachable servers. - pub failures: BTreeMap, - - /// One-time keys for the queried devices. - pub one_time_keys: BTreeMap, OneTimeKeys>, - } - - error: crate::Error -} - -impl Request { - /// Creates a new `Request` with the given key claims and the recommended 10 second timeout. - pub fn new( - one_time_keys: BTreeMap, BTreeMap, DeviceKeyAlgorithm>>, - ) -> Self { - Self { timeout: Some(Duration::from_secs(10)), one_time_keys } - } -} - -impl Response { - /// Creates a new `Response` with the given keys and no failures. - pub fn new(one_time_keys: BTreeMap, OneTimeKeys>) -> Self { - Self { failures: BTreeMap::new(), one_time_keys } - } -} - -/// The one-time keys for a given device. -pub type OneTimeKeys = BTreeMap, BTreeMap, Raw>>; diff --git a/crates/ruma-client-api/src/r0/keys/get_key_changes.rs b/crates/ruma-client-api/src/r0/keys/get_key_changes.rs deleted file mode 100644 index d449813e..00000000 --- a/crates/ruma-client-api/src/r0/keys/get_key_changes.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! [GET /_matrix/client/r0/keys/changes](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-keys-changes) - -use ruma_api::ruma_api; -use ruma_identifiers::UserId; - -ruma_api! { - metadata: { - description: "Gets a list of users who have updated their device identity keys since a previous sync token.", - method: GET, - name: "get_key_changes", - r0_path: "/_matrix/client/r0/keys/changes", - stable_path: "/_matrix/client/v3/keys/changes", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The desired start point of the list. - /// - /// Should be the next_batch field from a response to an earlier call to /sync. - #[ruma_api(query)] - pub from: &'a str, - - /// The desired end point of the list. - /// - /// Should be the next_batch field from a recent call to /sync - typically the most recent - /// such call. - #[ruma_api(query)] - pub to: &'a str, - } - - response: { - /// The Matrix User IDs of all users who updated their device identity keys. - pub changed: Vec>, - - /// The Matrix User IDs of all users who may have left all the end-to-end - /// encrypted rooms they previously shared with the user. - pub left: Vec>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given start and end points. - pub fn new(from: &'a str, to: &'a str) -> Self { - Self { from, to } - } -} - -impl Response { - /// Creates a new `Response` with the given changed and left user ID lists. - pub fn new(changed: Vec>, left: Vec>) -> Self { - Self { changed, left } - } -} diff --git a/crates/ruma-client-api/src/r0/keys/get_keys.rs b/crates/ruma-client-api/src/r0/keys/get_keys.rs deleted file mode 100644 index 4f31d5ae..00000000 --- a/crates/ruma-client-api/src/r0/keys/get_keys.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! [POST /_matrix/client/r0/keys/query](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-keys-query) - -use std::{collections::BTreeMap, time::Duration}; - -use ruma_api::ruma_api; -use ruma_common::encryption::{CrossSigningKey, DeviceKeys}; -use ruma_identifiers::{DeviceId, UserId}; -use ruma_serde::Raw; -use serde_json::Value as JsonValue; - -ruma_api! { - metadata: { - description: "Returns the current devices and identity keys for the given users.", - method: POST, - name: "get_keys", - r0_path: "/_matrix/client/r0/keys/query", - stable_path: "/_matrix/client/v3/keys/query", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: { - /// The time (in milliseconds) to wait when downloading keys from remote servers. - /// - /// 10 seconds is the recommended default. - #[serde( - with = "ruma_serde::duration::opt_ms", - default, - skip_serializing_if = "Option::is_none", - )] - pub timeout: Option, - - /// The keys to be downloaded. - /// - /// An empty list indicates all devices for the corresponding user. - pub device_keys: BTreeMap, Vec>>, - - /// If the client is fetching keys as a result of a device update received in a sync - /// request, this should be the 'since' token of that sync request, or any later sync token. - /// - /// This allows the server to ensure its response contains the keys advertised by the - /// notification in that sync. - #[serde(skip_serializing_if = "Option::is_none")] - pub token: Option<&'a str>, - } - - #[derive(Default)] - response: { - /// If any remote homeservers could not be reached, they are recorded here. - /// - /// The names of the properties are the names of the unreachable servers. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub failures: BTreeMap, - - /// Information on the queried devices. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub device_keys: BTreeMap, BTreeMap, Raw>>, - - /// Information on the master cross-signing keys of the queried users. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub master_keys: BTreeMap, Raw>, - - /// Information on the self-signing keys of the queried users. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub self_signing_keys: BTreeMap, Raw>, - - /// Information on the user-signing keys of the queried users. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub user_signing_keys: BTreeMap, Raw>, - } - - error: crate::Error -} - -impl Request<'_> { - /// Creates an empty `Request`. - pub fn new() -> Self { - Default::default() - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Default::default() - } -} diff --git a/crates/ruma-client-api/src/r0/keys/upload_keys.rs b/crates/ruma-client-api/src/r0/keys/upload_keys.rs deleted file mode 100644 index 7fd4b3c9..00000000 --- a/crates/ruma-client-api/src/r0/keys/upload_keys.rs +++ /dev/null @@ -1,61 +0,0 @@ -//! [POST /_matrix/client/r0/keys/upload](https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysupload) - -use std::collections::BTreeMap; - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_common::encryption::{DeviceKeys, OneTimeKey}; -use ruma_identifiers::{DeviceKeyAlgorithm, DeviceKeyId}; -use ruma_serde::Raw; - -ruma_api! { - metadata: { - description: "Publishes end-to-end encryption keys for the device.", - method: POST, - name: "upload_keys", - r0_path: "/_matrix/client/r0/keys/upload", - stable_path: "/_matrix/client/v3/keys/upload", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: { - /// Identity keys for the device. - /// - /// May be absent if no new identity keys are required. - #[serde(skip_serializing_if = "Option::is_none")] - pub device_keys: Option>, - - /// One-time public keys for "pre-key" messages. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub one_time_keys: BTreeMap, Raw>, - - /// Fallback public keys for "pre-key" messages. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty", rename = "org.matrix.msc2732.fallback_keys")] - pub fallback_keys: BTreeMap, Raw>, - } - - response: { - /// For each key algorithm, the number of unclaimed one-time keys of that - /// type currently held on the server for this device. - pub one_time_key_counts: BTreeMap, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Default::default() - } -} - -impl Response { - /// Creates a new `Response` with the given one time key counts. - pub fn new(one_time_key_counts: BTreeMap) -> Self { - Self { one_time_key_counts } - } -} diff --git a/crates/ruma-client-api/src/r0/keys/upload_signatures.rs b/crates/ruma-client-api/src/r0/keys/upload_signatures.rs deleted file mode 100644 index 8c2062e1..00000000 --- a/crates/ruma-client-api/src/r0/keys/upload_signatures.rs +++ /dev/null @@ -1,101 +0,0 @@ -//! [POST /_matrix/client/v3/keys/signatures/upload](https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keyssignaturesupload) - -use std::collections::BTreeMap; - -use ruma_api::ruma_api; -use ruma_common::encryption::{CrossSigningKey, DeviceKeys}; -use ruma_identifiers::{DeviceId, UserId}; -use ruma_serde::{Raw, StringEnum}; -use serde::{Deserialize, Serialize}; -use serde_json::value::RawValue as RawJsonValue; - -use crate::PrivOwnedStr; - -ruma_api! { - metadata: { - description: "Publishes cross-signing signatures for the user.", - method: POST, - name: "upload_signatures", - unstable_path: "/_matrix/client/unstable/keys/signatures/upload", - stable_path: "/_matrix/client/v3/keys/signatures/upload", - rate_limited: false, - authentication: AccessToken, - added: 1.1, - } - - request: { - /// Signed keys. - #[ruma_api(body)] - pub signed_keys: BTreeMap, SignedKeys>, - } - - #[derive(Default)] - response: { - /// Signature processing failures. - pub failures: BTreeMap, BTreeMap>, - } - - error: crate::Error -} - -impl Request { - /// Creates a new `Request` with the given signed keys. - pub fn new(signed_keys: BTreeMap, SignedKeys>) -> Self { - Self { signed_keys } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self::default() - } -} - -/// A map of key IDs to signed key objects. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(transparent)] -pub struct SignedKeys(BTreeMap, Box>); - -impl SignedKeys { - /// Creates an empty `SignedKeys` map. - pub fn new() -> Self { - Self::default() - } - - /// Add the given device keys. - pub fn add_device_keys(&mut self, device_id: Box, device_keys: Raw) { - self.0.insert(device_id.into(), device_keys.into_json()); - } - - /// Add the given cross signing keys. - pub fn add_cross_signing_keys( - &mut self, - cross_signing_key_id: Box, - cross_signing_keys: Raw, - ) { - self.0.insert(cross_signing_key_id, cross_signing_keys.into_json()); - } -} - -/// A failure to process a signed key. -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct Failure { - /// Machine-readable error code. - errcode: FailureErrorCode, - - /// Human-readable error message. - error: String, -} - -/// Error code for signed key processing failures. -#[derive(Clone, Debug, PartialEq, Eq, StringEnum)] -#[non_exhaustive] -#[ruma_enum(rename_all = "M_MATRIX_ERROR_CASE")] -pub enum FailureErrorCode { - /// The signature is invalid. - InvalidSignature, - - #[doc(hidden)] - _Custom(PrivOwnedStr), -} diff --git a/crates/ruma-client-api/src/r0/keys/upload_signing_keys.rs b/crates/ruma-client-api/src/r0/keys/upload_signing_keys.rs deleted file mode 100644 index 39c6f6f2..00000000 --- a/crates/ruma-client-api/src/r0/keys/upload_signing_keys.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! [POST /_matrix/client/v3/keys/device_signing/upload](https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3keysdevice_signingupload) - -use ruma_api::ruma_api; -use ruma_common::encryption::CrossSigningKey; -use ruma_serde::Raw; - -use crate::r0::uiaa::{AuthData, IncomingAuthData, UiaaResponse}; - -ruma_api! { - metadata: { - description: "Publishes cross signing keys for the user.", - method: POST, - name: "upload_signing_keys", - unstable_path: "/_matrix/client/unstable/keys/device_signing/upload", - stable_path: "/_matrix/client/v3/keys/device_signing/upload", - rate_limited: false, - authentication: AccessToken, - added: 1.1, - } - - #[derive(Default)] - request: { - /// Additional authentication information for the user-interactive authentication API. - #[serde(skip_serializing_if = "Option::is_none")] - pub auth: Option>, - - /// The user's master key. - #[serde(skip_serializing_if = "Option::is_none")] - pub master_key: Option>, - - /// The user's self-signing key. - /// - /// Must be signed with the accompanied master, or by the user's most recently uploaded - /// master key if no master key is included in the request. - #[serde(skip_serializing_if = "Option::is_none")] - pub self_signing_key: Option>, - - /// The user's user-signing key. - /// - /// Must be signed with the accompanied master, or by the user's most recently uploaded - /// master key if no master key is included in the request. - #[serde(skip_serializing_if = "Option::is_none")] - pub user_signing_key: Option>, - } - - #[derive(Default)] - response: {} - - error: UiaaResponse -} - -impl Request<'_> { - /// Creates an empty `Request`. - pub fn new() -> Self { - Default::default() - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/knock/knock_room.rs b/crates/ruma-client-api/src/r0/knock/knock_room.rs deleted file mode 100644 index 5472340a..00000000 --- a/crates/ruma-client-api/src/r0/knock/knock_room.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! [POST /_matrix/client/r0/knock/{roomIdOrAlias}](https://spec.matrix.org/unstable/client-server-api/#post_matrixclientr0knockroomidoralias) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomId, RoomOrAliasId, ServerName}; - -ruma_api! { - metadata: { - description: "Knock on a room.", - method: POST, - name: "knock_room", - unstable_path: "/_matrix/client/unstable/xyz.amorgan.knock/knock/:room_id_or_alias", - stable_path: "/_matrix/client/v3/knock/:room_id_or_alias", - rate_limited: true, - authentication: AccessToken, - added: 1.1, - } - - request: { - /// The room the user should knock on. - #[ruma_api(path)] - pub room_id_or_alias: &'a RoomOrAliasId, - - /// The reason for joining a room. - #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option<&'a str>, - - /// The servers to attempt to knock on the room through. - /// - /// One of the servers must be participating in the room. - #[ruma_api(query)] - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub server_name: &'a [Box], - } - - response: { - /// The room that the user knocked on. - pub room_id: Box, - } -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID or alias. - pub fn new(room_id_or_alias: &'a RoomOrAliasId) -> Self { - Self { room_id_or_alias, reason: None, server_name: &[] } - } -} - -impl Response { - /// Creates a new `Response` with the given room ID. - pub fn new(room_id: Box) -> Self { - Self { room_id } - } -} diff --git a/crates/ruma-client-api/src/r0/media/create_content.rs b/crates/ruma-client-api/src/r0/media/create_content.rs deleted file mode 100644 index 068e30e2..00000000 --- a/crates/ruma-client-api/src/r0/media/create_content.rs +++ /dev/null @@ -1,80 +0,0 @@ -//! [POST /_matrix/media/r0/upload](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-media-r0-upload) - -use ruma_api::ruma_api; -use ruma_identifiers::MxcUri; - -ruma_api! { - metadata: { - description: "Upload content to the media store.", - method: POST, - name: "create_media_content", - r0_path: "/_matrix/media/r0/upload", - stable_path: "/_matrix/media/v3/upload", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The file contents to upload. - #[ruma_api(raw_body)] - pub file: &'a [u8], - - /// The name of the file being uploaded. - #[ruma_api(query)] - #[serde(skip_serializing_if = "Option::is_none")] - pub filename: Option<&'a str>, - - /// The content type of the file being uploaded. - #[ruma_api(header = CONTENT_TYPE)] - pub content_type: Option<&'a str>, - - /// Should the server return a blurhash or not. - /// - /// This uses the unstable prefix in - /// [MSC2448](https://github.com/matrix-org/matrix-doc/pull/2448). - #[ruma_api(query)] - #[cfg(feature = "unstable-msc2448")] - #[serde(default, skip_serializing_if = "ruma_serde::is_default", rename = "xyz.amorgan.blurhash")] - pub generate_blurhash: bool, - } - - response: { - /// The MXC URI for the uploaded content. - pub content_uri: Box, - - /// The [BlurHash](https://blurha.sh) for the uploaded content. - /// - /// This uses the unstable prefix in - /// [MSC2448](https://github.com/matrix-org/matrix-doc/pull/2448). - #[cfg(feature = "unstable-msc2448")] - #[serde(rename = "xyz.amorgan.blurhash", skip_serializing_if = "Option::is_none")] - pub blurhash: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given file contents. - pub fn new(file: &'a [u8]) -> Self { - Self { - file, - filename: None, - content_type: None, - #[cfg(feature = "unstable-msc2448")] - generate_blurhash: false, - } - } -} - -impl Response { - /// Creates a new `Response` with the given MXC URI. - pub fn new(content_uri: Box) -> Self { - Self { - content_uri, - #[cfg(feature = "unstable-msc2448")] - blurhash: None, - } - } -} diff --git a/crates/ruma-client-api/src/r0/media/get_content.rs b/crates/ruma-client-api/src/r0/media/get_content.rs deleted file mode 100644 index 8c6b1799..00000000 --- a/crates/ruma-client-api/src/r0/media/get_content.rs +++ /dev/null @@ -1,76 +0,0 @@ -//! [GET /_matrix/media/r0/download/{serverName}/{mediaId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-download-servername-mediaid) - -use ruma_api::ruma_api; -use ruma_identifiers::{Error, MxcUri, ServerName}; - -ruma_api! { - metadata: { - description: "Retrieve content from the media store.", - method: GET, - name: "get_media_content", - r0_path: "/_matrix/media/r0/download/:server_name/:media_id", - stable_path: "/_matrix/media/v3/download/:server_name/:media_id", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// The media ID from the mxc:// URI (the path component). - #[ruma_api(path)] - pub media_id: &'a str, - - /// The server name from the mxc:// URI (the authoritory component). - #[ruma_api(path)] - pub server_name: &'a ServerName, - - /// Whether to fetch media deemed remote. - /// - /// Used to prevent routing loops. Defaults to `true`. - #[ruma_api(query)] - #[serde(default = "ruma_serde::default_true", skip_serializing_if = "ruma_serde::is_true")] - pub allow_remote: bool, - } - - response: { - /// The content that was previously uploaded. - #[ruma_api(raw_body)] - pub file: Vec, - - /// The content type of the file that was previously uploaded. - #[ruma_api(header = CONTENT_TYPE)] - pub content_type: Option, - - /// The value of the `Content-Disposition` HTTP header, possibly containing the name of the - /// file that was previously uploaded. - /// - /// See [MDN] for the syntax. - /// - /// [MDN]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Syntax - #[ruma_api(header = CONTENT_DISPOSITION)] - pub content_disposition: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given media ID and server name. - pub fn new(media_id: &'a str, server_name: &'a ServerName) -> Self { - Self { media_id, server_name, allow_remote: true } - } - - /// Creates a new `Request` with the given url. - pub fn from_url(url: &'a MxcUri) -> Result { - let (server_name, media_id) = url.parts()?; - - Ok(Self { media_id, server_name, allow_remote: true }) - } -} - -impl Response { - /// Creates a new `Response` with the given file contents. - pub fn new(file: Vec) -> Self { - Self { file, content_type: None, content_disposition: None } - } -} diff --git a/crates/ruma-client-api/src/r0/media/get_content_as_filename.rs b/crates/ruma-client-api/src/r0/media/get_content_as_filename.rs deleted file mode 100644 index ffe92bce..00000000 --- a/crates/ruma-client-api/src/r0/media/get_content_as_filename.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! [GET /_matrix/media/r0/download/{serverName}/{mediaId}/{fileName}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-download-servername-mediaid-filename) - -use ruma_api::ruma_api; -use ruma_identifiers::{Error, MxcUri, ServerName}; - -ruma_api! { - metadata: { - description: "Retrieve content from the media store, specifying a filename to return.", - method: GET, - name: "get_media_content_as_filename", - r0_path: "/_matrix/media/r0/download/:server_name/:media_id/:filename", - stable_path: "/_matrix/media/v3/download/:server_name/:media_id/:filename", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// The media ID from the mxc:// URI (the path component). - #[ruma_api(path)] - pub media_id: &'a str, - - /// The server name from the mxc:// URI (the authoritory component). - #[ruma_api(path)] - pub server_name: &'a ServerName, - - /// The filename to return in the `Content-Disposition` header. - #[ruma_api(path)] - pub filename: &'a str, - - /// Whether to fetch media deemed remote. - /// - /// Used to prevent routing loops. Defaults to `true`. - #[ruma_api(query)] - #[serde(default = "ruma_serde::default_true", skip_serializing_if = "ruma_serde::is_true")] - pub allow_remote: bool, - } - - response: { - /// The content that was previously uploaded. - #[ruma_api(raw_body)] - pub file: Vec, - - /// The content type of the file that was previously uploaded. - #[ruma_api(header = CONTENT_TYPE)] - // Potentially not actually optional – https://github.com/matrix-org/matrix-doc/pull/2818 - pub content_type: Option, - - /// The value of the `Content-Disposition` HTTP header, possibly containing the name of the - /// file that was previously uploaded. - /// - /// See [MDN] for the syntax. - /// - /// [MDN]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Disposition#Syntax - #[ruma_api(header = CONTENT_DISPOSITION)] - pub content_disposition: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given media ID, server name and filename. - pub fn new(media_id: &'a str, server_name: &'a ServerName, filename: &'a str) -> Self { - Self { media_id, server_name, filename, allow_remote: true } - } - - /// Creates a new `Request` with the given url and filename. - pub fn from_url(url: &'a MxcUri, filename: &'a str) -> Result { - let (server_name, media_id) = url.parts()?; - - Ok(Self { media_id, server_name, filename, allow_remote: true }) - } -} - -impl Response { - /// Creates a new `Response` with the given file. - pub fn new(file: Vec) -> Self { - Self { file, content_type: None, content_disposition: None } - } -} diff --git a/crates/ruma-client-api/src/r0/media/get_content_thumbnail.rs b/crates/ruma-client-api/src/r0/media/get_content_thumbnail.rs deleted file mode 100644 index dd81cee7..00000000 --- a/crates/ruma-client-api/src/r0/media/get_content_thumbnail.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! [GET /_matrix/media/r0/thumbnail/{serverName}/{mediaId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-thumbnail-servername-mediaid) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_identifiers::{Error, MxcUri, ServerName}; -use ruma_serde::StringEnum; - -use crate::PrivOwnedStr; - -ruma_api! { - metadata: { - description: "Get a thumbnail of content from the media store.", - method: GET, - name: "get_content_thumbnail", - r0_path: "/_matrix/media/r0/thumbnail/:server_name/:media_id", - stable_path: "/_matrix/media/v3/thumbnail/:server_name/:media_id", - rate_limited: true, - authentication: None, - added: 1.0, - } - - request: { - /// The media ID from the mxc:// URI (the path component). - #[ruma_api(path)] - pub media_id: &'a str, - - /// The server name from the mxc:// URI (the authoritory component). - #[ruma_api(path)] - pub server_name: &'a ServerName, - - /// The desired resizing method. - #[ruma_api(query)] - #[serde(skip_serializing_if = "Option::is_none")] - pub method: Option, - - /// The *desired* width of the thumbnail. - /// - /// The actual thumbnail may not match the size specified. - #[ruma_api(query)] - pub width: UInt, - - /// The *desired* height of the thumbnail. - /// - /// The actual thumbnail may not match the size specified. - #[ruma_api(query)] - pub height: UInt, - - /// Whether to fetch media deemed remote. - /// - /// Used to prevent routing loops. Defaults to `true`. - #[ruma_api(query)] - #[serde(default = "ruma_serde::default_true", skip_serializing_if = "ruma_serde::is_true")] - pub allow_remote: bool, - } - - response: { - /// A thumbnail of the requested content. - #[ruma_api(raw_body)] - pub file: Vec, - - /// The content type of the thumbnail. - #[ruma_api(header = CONTENT_TYPE)] - pub content_type: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given media ID, server name, desired thumbnail width and - /// desired thumbnail height. - pub fn new(media_id: &'a str, server_name: &'a ServerName, width: UInt, height: UInt) -> Self { - Self { media_id, server_name, method: None, width, height, allow_remote: true } - } - - /// Creates a new `Request` with the given url, desired thumbnail width and - /// desired thumbnail height. - pub fn from_url(url: &'a MxcUri, width: UInt, height: UInt) -> Result { - let (server_name, media_id) = url.parts()?; - - Ok(Self { media_id, server_name, method: None, width, height, allow_remote: true }) - } -} - -impl Response { - /// Creates a new `Response` with the given thumbnail. - pub fn new(file: Vec) -> Self { - Self { file, content_type: None } - } -} - -/// The desired resizing method. -/// -/// This type can hold an arbitrary string. To check for formats that are not available as a -/// documented variant here, use its string representation, obtained through `.as_str()`. -#[derive(Clone, Debug, StringEnum)] -#[ruma_enum(rename_all = "snake_case")] -#[non_exhaustive] -pub enum Method { - /// Crop the original to produce the requested image dimensions. - Crop, - - /// Maintain the original aspect ratio of the source image. - Scale, - - #[doc(hidden)] - _Custom(PrivOwnedStr), -} - -impl Method { - /// Creates a string slice from this `Method`. - pub fn as_str(&self) -> &str { - self.as_ref() - } -} diff --git a/crates/ruma-client-api/src/r0/media/get_media_config.rs b/crates/ruma-client-api/src/r0/media/get_media_config.rs deleted file mode 100644 index b3cc1aee..00000000 --- a/crates/ruma-client-api/src/r0/media/get_media_config.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! [GET /_matrix/media/r0/config](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-config) - -use js_int::UInt; -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "Gets the config for the media repository.", - method: GET, - r0_path: "/_matrix/media/r0/config", - stable_path: "/_matrix/media/v3/config", - name: "get_media_config", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// Maximum size of upload in bytes. - #[serde(rename = "m.upload.size")] - pub upload_size: UInt, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given maximum upload size. - pub fn new(upload_size: UInt) -> Self { - Self { upload_size } - } -} diff --git a/crates/ruma-client-api/src/r0/media/get_media_preview.rs b/crates/ruma-client-api/src/r0/media/get_media_preview.rs deleted file mode 100644 index d05bc479..00000000 --- a/crates/ruma-client-api/src/r0/media/get_media_preview.rs +++ /dev/null @@ -1,93 +0,0 @@ -//! [GET /_matrix/media/r0/preview_url](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-media-r0-preview-url) - -use ruma_api::ruma_api; -use ruma_common::MilliSecondsSinceUnixEpoch; -use serde::Serialize; -use serde_json::value::{to_raw_value as to_raw_json_value, RawValue as RawJsonValue}; - -ruma_api! { - metadata: { - description: "Get a preview for a URL.", - name: "get_media_preview", - method: GET, - r0_path: "/_matrix/media/r0/preview_url", - stable_path: "/_matrix/media/v3/preview_url", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// URL to get a preview of. - #[ruma_api(query)] - pub url: &'a str, - - /// Preferred point in time (in milliseconds) to return a preview for. - #[ruma_api(query)] - pub ts: MilliSecondsSinceUnixEpoch, - } - - #[derive(Default)] - response: { - /// OpenGraph-like data for the URL. - /// - /// Differences from OpenGraph: the image size in bytes is added to the `matrix:image:size` - /// field, and `og:image` returns the MXC URI to the image, if any. - #[ruma_api(body)] - pub data: Option>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given url and timestamp. - pub fn new(url: &'a str, ts: MilliSecondsSinceUnixEpoch) -> Self { - Self { url, ts } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self { data: None } - } - - /// Creates a new `Response` with the given OpenGraph data (in a `serde_json::value::RawValue`). - pub fn from_raw_value(data: Box) -> Self { - Self { data: Some(data) } - } - - /// Creates a new `Response` with the given OpenGraph data (in any kind of serializable object). - pub fn from_serialize(data: &T) -> serde_json::Result { - Ok(Self { data: Some(to_raw_json_value(data)?) }) - } -} - -#[cfg(test)] -mod tests { - use serde_json::{ - from_value as from_json_value, json, - value::{to_raw_value as to_raw_json_value, RawValue as RawJsonValue}, - }; - - // Since BTreeMap> deserialization doesn't seem to - // work, test that Option works - #[test] - fn raw_json_deserialize() { - type OptRawJson = Option>; - - assert!(from_json_value::(json!(null)).unwrap().is_none()); - assert!(from_json_value::(json!("test")).unwrap().is_some()); - assert!(from_json_value::(json!({ "a": "b" })).unwrap().is_some()); - } - - // For completeness sake, make sure serialization works too - #[test] - fn raw_json_serialize() { - assert!(to_raw_json_value(&json!(null)).is_ok()); - assert!(to_raw_json_value(&json!("string")).is_ok()); - assert!(to_raw_json_value(&json!({})).is_ok()); - assert!(to_raw_json_value(&json!({ "a": "b" })).is_ok()); - } -} diff --git a/crates/ruma-client-api/src/r0/membership/ban_user.rs b/crates/ruma-client-api/src/r0/membership/ban_user.rs deleted file mode 100644 index 6f738538..00000000 --- a/crates/ruma-client-api/src/r0/membership/ban_user.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/ban](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-ban) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomId, UserId}; - -ruma_api! { - metadata: { - description: "Ban a user from a room.", - method: POST, - name: "ban_user", - r0_path: "/_matrix/client/r0/rooms/:room_id/ban", - stable_path: "/_matrix/client/v3/rooms/:room_id/ban", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to kick the user from. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The user to ban. - pub user_id: &'a UserId, - - /// The reason for banning the user. - #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option<&'a str>, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room id and room id. - pub fn new(room_id: &'a RoomId, user_id: &'a UserId) -> Self { - Self { room_id, user_id, reason: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/membership/forget_room.rs b/crates/ruma-client-api/src/r0/membership/forget_room.rs deleted file mode 100644 index fd361e93..00000000 --- a/crates/ruma-client-api/src/r0/membership/forget_room.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/forget](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-forget) - -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -ruma_api! { - metadata: { - description: "Forget a room.", - method: POST, - name: "forget_room", - r0_path: "/_matrix/client/r0/rooms/:room_id/forget", - stable_path: "/_matrix/client/v3/rooms/:room_id/forget", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to forget. - #[ruma_api(path)] - pub room_id: &'a RoomId, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room id. - pub fn new(room_id: &'a RoomId) -> Self { - Self { room_id } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/membership/get_member_events.rs b/crates/ruma-client-api/src/r0/membership/get_member_events.rs deleted file mode 100644 index ef4bc876..00000000 --- a/crates/ruma-client-api/src/r0/membership/get_member_events.rs +++ /dev/null @@ -1,140 +0,0 @@ -//! [GET /_matrix/client/r0/rooms/{roomId}/members](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-members) - -use ruma_api::ruma_api; -use ruma_events::room::member::RoomMemberEvent; -use ruma_identifiers::RoomId; -use ruma_serde::{Raw, StringEnum}; - -use crate::PrivOwnedStr; - -ruma_api! { - metadata: { - description: "Get membership events for a room.", - method: GET, - name: "get_member_events", - r0_path: "/_matrix/client/r0/rooms/:room_id/members", - stable_path: "/_matrix/client/v3/rooms/:room_id/members", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to get the member events for. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The point in time (pagination token) to return members for in the room. - /// - /// This token can be obtained from a prev_batch token returned for each room by the sync - /// API. - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub at: Option<&'a str>, - - /// The kind of memberships to filter for. - /// - /// Defaults to no filtering if unspecified. When specified alongside not_membership, the - /// two parameters create an 'or' condition: either the membership is the same as membership - /// or is not the same as not_membership. - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub membership: Option, - - /// The kind of memberships to *exclude* from the results. - /// - /// Defaults to no filtering if unspecified. - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub not_membership: Option, - } - - response: { - /// A list of member events. - pub chunk: Vec>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID. - pub fn new(room_id: &'a RoomId) -> Self { - Self { room_id, at: None, membership: None, not_membership: None } - } -} - -impl Response { - /// Creates a new `Response` with the given member event chunk. - pub fn new(chunk: Vec>) -> Self { - Self { chunk } - } -} - -/// The kind of membership events to filter for. -/// -/// This type can hold an arbitrary string. To check for formats that are not available as a -/// documented variant here, use its string representation, obtained through `.as_str()`. -#[derive(Clone, Debug, PartialEq, Eq, StringEnum)] -#[ruma_enum(rename_all = "lowercase")] -#[non_exhaustive] -pub enum MembershipEventFilter { - /// The user has joined. - Join, - - /// The user has been invited. - Invite, - - /// The user has left. - Leave, - - /// The user has been banned. - Ban, - - #[doc(hidden)] - _Custom(PrivOwnedStr), -} - -impl MembershipEventFilter { - /// Creates a string slice from this `MembershipEventFilter`. - pub fn as_str(&self) -> &str { - self.as_ref() - } -} - -#[cfg(all(test, feature = "server"))] -mod tests { - use matches::assert_matches; - use ruma_api::IncomingRequest as _; - - use super::{IncomingRequest, MembershipEventFilter}; - - #[test] - fn deserialization() { - let uri = http::Uri::builder() - .scheme("https") - .authority("example.org") - .path_and_query( - "/_matrix/client/r0/rooms/!dummy%3Aexample.org/members\ - ?not_membership=leave\ - &at=1026", - ) - .build() - .unwrap(); - - let req = IncomingRequest::try_from_http_request( - http::Request::builder().uri(uri).body(&[] as &[u8]).unwrap(), - &["!dummy:example.org"], - ); - - assert_matches!( - req, - Ok(IncomingRequest { - room_id, - at: Some(at), - membership: None, - not_membership: Some(MembershipEventFilter::Leave), - }) if room_id == "!dummy:example.org" && at == "1026" - ); - } -} diff --git a/crates/ruma-client-api/src/r0/membership/invite_user.rs b/crates/ruma-client-api/src/r0/membership/invite_user.rs deleted file mode 100644 index fafc9926..00000000 --- a/crates/ruma-client-api/src/r0/membership/invite_user.rs +++ /dev/null @@ -1,116 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/invite][invite-by-user-id] -//! -//! This endpoint has two forms: one to invite a user -//! [by their Matrix identifier][invite-by-user-id], and one to invite a user -//! [by their third party identifier][invite-by-3pid]. -//! -//! [invite-by-user-id]: https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-invite -//! [invite-by-3pid]: https://matrix.org/docs/spec/client_server/r0.6.1#id101 - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomId, UserId}; -use ruma_serde::Outgoing; -use serde::Serialize; - -use super::{IncomingInvite3pid, Invite3pid}; - -ruma_api! { - metadata: { - description: "Invite a user to a room.", - method: POST, - name: "invite_user", - r0_path: "/_matrix/client/r0/rooms/:room_id/invite", - stable_path: "/_matrix/client/v3/rooms/:room_id/invite", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room where the user should be invited. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The user to invite. - #[serde(flatten)] - pub recipient: InvitationRecipient<'a>, - - /// Optional reason for inviting the user. - #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option<&'a str>, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID and invitation recipient. - pub fn new(room_id: &'a RoomId, recipient: InvitationRecipient<'a>) -> Self { - Self { room_id, recipient, reason: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} - -/// Distinguishes between invititations by Matrix or third party identifiers. -#[derive(Clone, Debug, PartialEq, Outgoing, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[incoming_derive(PartialEq)] -#[serde(untagged)] -pub enum InvitationRecipient<'a> { - /// Used to invite user by their Matrix identifier. - UserId { - /// Matrix identifier of user. - user_id: &'a UserId, - }, - - /// Used to invite user by a third party identifier. - ThirdPartyId(Invite3pid<'a>), -} - -#[cfg(test)] -mod tests { - use ruma_common::thirdparty::Medium; - use ruma_identifiers::user_id; - use serde_json::{from_value as from_json_value, json}; - - use super::IncomingInvitationRecipient; - use crate::r0::membership::IncomingInvite3pid; - - #[test] - fn deserialize_invite_by_user_id() { - let incoming = from_json_value::( - json!({ "user_id": "@carl:example.org" }), - ) - .unwrap(); - let user_id = user_id!("@carl:example.org").to_owned(); - let recipient = IncomingInvitationRecipient::UserId { user_id }; - assert_eq!(incoming, recipient); - } - - #[test] - fn deserialize_invite_by_3pid() { - let incoming = from_json_value::(json!({ - "id_server": "example.org", - "id_access_token": "abcdefghijklmnop", - "medium": "email", - "address": "carl@example.org" - })) - .unwrap(); - let recipient = IncomingInvitationRecipient::ThirdPartyId(IncomingInvite3pid { - id_server: "example.org".into(), - id_access_token: "abcdefghijklmnop".into(), - medium: Medium::Email, - address: "carl@example.org".into(), - }); - assert_eq!(incoming, recipient); - } -} diff --git a/crates/ruma-client-api/src/r0/membership/join_room_by_id.rs b/crates/ruma-client-api/src/r0/membership/join_room_by_id.rs deleted file mode 100644 index 55bf85c0..00000000 --- a/crates/ruma-client-api/src/r0/membership/join_room_by_id.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/join](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-join) - -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -use super::{IncomingThirdPartySigned, ThirdPartySigned}; - -ruma_api! { - metadata: { - description: "Join a room using its ID.", - method: POST, - name: "join_room_by_id", - r0_path: "/_matrix/client/r0/rooms/:room_id/join", - stable_path: "/_matrix/client/v3/rooms/:room_id/join", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room where the user should be invited. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The signature of a `m.third_party_invite` token to prove that this user owns a third - /// party identity which has been invited to the room. - #[serde(skip_serializing_if = "Option::is_none")] - pub third_party_signed: Option>, - - /// Optional reason for joining the room. - #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option<&'a str>, - } - - response: { - /// The room that the user joined. - pub room_id: Box, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room id. - pub fn new(room_id: &'a RoomId) -> Self { - Self { room_id, third_party_signed: None, reason: None } - } -} - -impl Response { - /// Creates a new `Response` with the given room id. - pub fn new(room_id: Box) -> Self { - Self { room_id } - } -} diff --git a/crates/ruma-client-api/src/r0/membership/join_room_by_id_or_alias.rs b/crates/ruma-client-api/src/r0/membership/join_room_by_id_or_alias.rs deleted file mode 100644 index 3f47262e..00000000 --- a/crates/ruma-client-api/src/r0/membership/join_room_by_id_or_alias.rs +++ /dev/null @@ -1,62 +0,0 @@ -//! [POST /_matrix/client/r0/join/{roomIdOrAlias}](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-join-roomidoralias) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomId, RoomOrAliasId, ServerName}; - -use super::{IncomingThirdPartySigned, ThirdPartySigned}; - -ruma_api! { - metadata: { - description: "Join a room using its ID or one of its aliases.", - method: POST, - name: "join_room_by_id_or_alias", - r0_path: "/_matrix/client/r0/join/:room_id_or_alias", - stable_path: "/_matrix/client/v3/join/:room_id_or_alias", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room where the user should be invited. - #[ruma_api(path)] - pub room_id_or_alias: &'a RoomOrAliasId, - - /// The servers to attempt to join the room through. - /// - /// One of the servers must be participating in the room. - #[ruma_api(query)] - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub server_name: &'a [Box], - - /// The signature of a `m.third_party_invite` token to prove that this user owns a third - /// party identity which has been invited to the room. - #[serde(skip_serializing_if = "Option::is_none")] - pub third_party_signed: Option>, - - /// Optional reason for joining the room. - #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option<&'a str>, - } - - response: { - /// The room that the user joined. - pub room_id: Box, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID or alias ID. - pub fn new(room_id_or_alias: &'a RoomOrAliasId) -> Self { - Self { room_id_or_alias, server_name: &[], third_party_signed: None, reason: None } - } -} - -impl Response { - /// Creates a new `Response` with the given room ID. - pub fn new(room_id: Box) -> Self { - Self { room_id } - } -} diff --git a/crates/ruma-client-api/src/r0/membership/joined_members.rs b/crates/ruma-client-api/src/r0/membership/joined_members.rs deleted file mode 100644 index 2c9266b5..00000000 --- a/crates/ruma-client-api/src/r0/membership/joined_members.rs +++ /dev/null @@ -1,109 +0,0 @@ -//! [GET /_matrix/client/r0/rooms/{roomId}/joined_members](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-joined-members) - -use std::collections::BTreeMap; - -use ruma_api::ruma_api; -use ruma_identifiers::{MxcUri, RoomId, UserId}; -use serde::{Deserialize, Serialize}; - -ruma_api! { - metadata: { - description: "Get a map of user ids to member info objects for members of the room. Primarily for use in Application Services.", - method: GET, - name: "joined_members", - r0_path: "/_matrix/client/r0/rooms/:room_id/joined_members", - stable_path: "/_matrix/client/v3/rooms/:room_id/joined_members", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to get the members of. - #[ruma_api(path)] - pub room_id: &'a RoomId, - } - - response: { - /// A list of the rooms the user is in, i.e. - /// the ID of each room in which the user has joined membership. - pub joined: BTreeMap, RoomMember>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID. - pub fn new(room_id: &'a RoomId) -> Self { - Self { room_id } - } -} - -impl Response { - /// Creates a new `Response` with the given joined rooms. - pub fn new(joined: BTreeMap, RoomMember>) -> Self { - Self { joined } - } -} - -/// Information about a room member. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct RoomMember { - /// The display name of the user. - #[serde(skip_serializing_if = "Option::is_none")] - pub display_name: Option, - - /// The mxc avatar url of the user. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will result - /// in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub avatar_url: Option>, -} - -impl RoomMember { - /// Creates an empty `RoomMember`. - pub fn new() -> Self { - Default::default() - } -} - -#[cfg(test)] -mod test { - use super::RoomMember; - use matches::assert_matches; - use serde_json::{from_value as from_json_value, json}; - - #[test] - fn deserialize_room_member() { - assert_matches!( - from_json_value::(json!({ - "display_name": "alice", - "avatar_url": "mxc://localhost/wefuiwegh8742w", - })).unwrap(), - RoomMember { - display_name: Some(display_name), - avatar_url: Some(avatar_url), - } if display_name == "alice" - && avatar_url == "mxc://localhost/wefuiwegh8742w" - ); - - #[cfg(feature = "compat")] - assert_matches!( - from_json_value::(json!({ - "display_name": "alice", - "avatar_url": "", - })).unwrap(), - RoomMember { - display_name: Some(display_name), - avatar_url: None, - } if display_name == "alice" - ); - } -} diff --git a/crates/ruma-client-api/src/r0/membership/joined_rooms.rs b/crates/ruma-client-api/src/r0/membership/joined_rooms.rs deleted file mode 100644 index c6b4513d..00000000 --- a/crates/ruma-client-api/src/r0/membership/joined_rooms.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! [GET /_matrix/client/r0/joined_rooms](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-joined-rooms) - -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -ruma_api! { - metadata: { - description: "Get a list of the user's current rooms.", - method: GET, - name: "joined_rooms", - r0_path: "/_matrix/client/r0/joined_rooms", - stable_path: "/_matrix/client/v3/joined_rooms", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// A list of the rooms the user is in, i.e. the ID of each room in - /// which the user has joined membership. - pub joined_rooms: Vec>, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given joined rooms. - pub fn new(joined_rooms: Vec>) -> Self { - Self { joined_rooms } - } -} diff --git a/crates/ruma-client-api/src/r0/membership/kick_user.rs b/crates/ruma-client-api/src/r0/membership/kick_user.rs deleted file mode 100644 index 5b7acb21..00000000 --- a/crates/ruma-client-api/src/r0/membership/kick_user.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/kick](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-kick) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomId, UserId}; - -ruma_api! { - metadata: { - description: "Kick a user from a room.", - method: POST, - name: "kick_user", - r0_path: "/_matrix/client/r0/rooms/:room_id/kick", - stable_path: "/_matrix/client/v3/rooms/:room_id/kick", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to kick the user from. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The user to kick. - pub user_id: &'a UserId, - - /// The reason for kicking the user. - #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option<&'a str>, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room id and room id. - pub fn new(room_id: &'a RoomId, user_id: &'a UserId) -> Self { - Self { room_id, user_id, reason: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/membership/leave_room.rs b/crates/ruma-client-api/src/r0/membership/leave_room.rs deleted file mode 100644 index dcb7d420..00000000 --- a/crates/ruma-client-api/src/r0/membership/leave_room.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/leave](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-leave) - -use ruma_api::ruma_api; -use ruma_identifiers::RoomId; - -ruma_api! { - metadata: { - description: "Leave a room.", - method: POST, - name: "leave_room", - r0_path: "/_matrix/client/r0/rooms/:room_id/leave", - stable_path: "/_matrix/client/v3/rooms/:room_id/leave", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to leave. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// Optional reason to be included as the `reason` on the subsequent membership event. - #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option<&'a str>, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room id. - pub fn new(room_id: &'a RoomId) -> Self { - Self { room_id, reason: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/membership/unban_user.rs b/crates/ruma-client-api/src/r0/membership/unban_user.rs deleted file mode 100644 index 54376931..00000000 --- a/crates/ruma-client-api/src/r0/membership/unban_user.rs +++ /dev/null @@ -1,49 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/unban](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-unban) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomId, UserId}; - -ruma_api! { - metadata: { - description: "Unban a user from a room.", - method: POST, - name: "unban_user", - r0_path: "/_matrix/client/r0/rooms/:room_id/unban", - stable_path: "/_matrix/client/v3/rooms/:room_id/unban", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to unban the user from. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The user to unban. - pub user_id: &'a UserId, - - /// Optional reason for unbanning the user. - #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option<&'a str>, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room id and room id. - pub fn new(room_id: &'a RoomId, user_id: &'a UserId) -> Self { - Self { room_id, user_id, reason: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/message/get_message_events.rs b/crates/ruma-client-api/src/r0/message/get_message_events.rs deleted file mode 100644 index 2d247b3c..00000000 --- a/crates/ruma-client-api/src/r0/message/get_message_events.rs +++ /dev/null @@ -1,214 +0,0 @@ -//! [GET /_matrix/client/r0/rooms/{roomId}/messages](https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3roomsroomidmessages) - -use js_int::{uint, UInt}; -use ruma_api::ruma_api; -use ruma_events::{AnyRoomEvent, AnyStateEvent}; -use ruma_identifiers::RoomId; -use ruma_serde::Raw; -use serde::{Deserialize, Serialize}; - -use crate::r0::filter::{IncomingRoomEventFilter, RoomEventFilter}; - -ruma_api! { - metadata: { - description: "Get message events for a room.", - method: GET, - name: "get_message_events", - r0_path: "/_matrix/client/r0/rooms/:room_id/messages", - stable_path: "/_matrix/client/v3/rooms/:room_id/messages", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to get events from. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The token to start returning events from. - /// - /// This token can be obtained from a - /// prev_batch token returned for each room by the sync API, or from a start or end token - /// returned by a previous request to this endpoint. - #[ruma_api(query)] - pub from: &'a str, - - /// The token to stop returning events at. - /// - /// This token can be obtained from a prev_batch - /// token returned for each room by the sync endpoint, or from a start or end token returned - /// by a previous request to this endpoint. - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub to: Option<&'a str>, - - /// The direction to return events from. - #[ruma_api(query)] - pub dir: Direction, - - /// The maximum number of events to return. - /// - /// Default: 10. - #[ruma_api(query)] - #[serde(default = "default_limit", skip_serializing_if = "is_default_limit")] - pub limit: UInt, - - /// A RoomEventFilter to filter returned events with. - #[ruma_api(query)] - #[serde( - with = "ruma_serde::json_string", - default, - skip_serializing_if = "RoomEventFilter::is_empty" - )] - pub filter: RoomEventFilter<'a>, - } - - #[derive(Default)] - response: { - /// The token the pagination starts from. - pub start: String, - - /// The token the pagination ends at. - #[serde(skip_serializing_if = "Option::is_none")] - pub end: Option, - - /// A list of room events. - #[serde(default)] - pub chunk: Vec>, - - /// A list of state events relevant to showing the `chunk`. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub state: Vec>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given parameters. - /// - /// All other parameters will be defaulted. - pub fn new(room_id: &'a RoomId, from: &'a str, dir: Direction) -> Self { - Self { - room_id, - from, - to: None, - dir, - limit: default_limit(), - filter: RoomEventFilter::default(), - } - } - - /// Creates a new `Request` with the given room ID and from token, and `dir` set to `Backward`. - pub fn backward(room_id: &'a RoomId, from: &'a str) -> Self { - Self::new(room_id, from, Direction::Backward) - } - - /// Creates a new `Request` with the given room ID and from token, and `dir` set to `Forward`. - pub fn forward(room_id: &'a RoomId, from: &'a str) -> Self { - Self::new(room_id, from, Direction::Forward) - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Default::default() - } -} - -fn default_limit() -> UInt { - uint!(10) -} - -#[allow(clippy::trivially_copy_pass_by_ref)] -fn is_default_limit(val: &UInt) -> bool { - *val == default_limit() -} - -/// The direction to return events from. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[allow(clippy::exhaustive_enums)] -pub enum Direction { - /// Return events backwards in time from the requested `from` token. - #[serde(rename = "b")] - Backward, - - /// Return events forwards in time from the requested `from` token. - #[serde(rename = "f")] - Forward, -} - -#[cfg(all(test, feature = "client"))] -mod tests { - use js_int::uint; - use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken}; - use ruma_identifiers::room_id; - - use super::{Direction, Request}; - use crate::r0::filter::{LazyLoadOptions, RoomEventFilter}; - - #[test] - fn serialize_some_room_event_filter() { - let room_id = room_id!("!roomid:example.org"); - let rooms = &[room_id.to_owned()]; - let filter = RoomEventFilter { - lazy_load_options: LazyLoadOptions::Enabled { include_redundant_members: true }, - rooms: Some(rooms), - not_rooms: &[ - room_id!("!room:example.org").to_owned(), - room_id!("!room2:example.org").to_owned(), - room_id!("!room3:example.org").to_owned(), - ], - not_types: &["type".into()], - ..Default::default() - }; - let req = Request { - room_id, - from: "token", - to: Some("token2"), - dir: Direction::Backward, - limit: uint!(0), - filter, - }; - - let request: http::Request> = req - .try_into_http_request( - "https://homeserver.tld", - SendAccessToken::IfRequired("auth_tok"), - &[MatrixVersion::V1_1], - ) - .unwrap(); - assert_eq!( - "from=token\ - &to=token2\ - &dir=b\ - &limit=0\ - &filter=%7B%22not_types%22%3A%5B%22type%22%5D%2C%22not_rooms%22%3A%5B%22%21room%3Aexample.org%22%2C%22%21room2%3Aexample.org%22%2C%22%21room3%3Aexample.org%22%5D%2C%22rooms%22%3A%5B%22%21roomid%3Aexample.org%22%5D%2C%22lazy_load_members%22%3Atrue%2C%22include_redundant_members%22%3Atrue%7D", - request.uri().query().unwrap() - ); - } - - #[test] - fn serialize_default_room_event_filter() { - let room_id = room_id!("!roomid:example.org"); - let req = Request { - room_id, - from: "token", - to: Some("token2"), - dir: Direction::Backward, - limit: uint!(0), - filter: RoomEventFilter::default(), - }; - - let request = req - .try_into_http_request::>( - "https://homeserver.tld", - SendAccessToken::IfRequired("auth_tok"), - &[MatrixVersion::V1_1], - ) - .unwrap(); - assert_eq!("from=token&to=token2&dir=b&limit=0", request.uri().query().unwrap(),); - } -} diff --git a/crates/ruma-client-api/src/r0/message/send_message_event.rs b/crates/ruma-client-api/src/r0/message/send_message_event.rs deleted file mode 100644 index 4c360584..00000000 --- a/crates/ruma-client-api/src/r0/message/send_message_event.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! [PUT /_matrix/client/r0/rooms/{roomId}/send/{eventType}/{txnId}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-send-eventtype-txnid) - -use ruma_api::ruma_api; -use ruma_events::{AnyMessageEventContent, MessageEventContent}; -use ruma_identifiers::{EventId, RoomId, TransactionId}; -use ruma_serde::Raw; -use serde_json::value::to_raw_value as to_raw_json_value; - -ruma_api! { - metadata: { - description: "Send a message event to a room.", - method: PUT, - name: "create_message_event", - r0_path: "/_matrix/client/r0/rooms/:room_id/send/:event_type/:txn_id", - stable_path: "/_matrix/client/v3/rooms/:room_id/send/:event_type/:txn_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to send the event to. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The type of event to send. - #[ruma_api(path)] - pub event_type: &'a str, - - /// The transaction ID for this event. - /// - /// Clients should generate an ID unique across requests with the - /// same access token; it will be used by the server to ensure - /// idempotency of requests. - #[ruma_api(path)] - pub txn_id: &'a TransactionId, - - /// The event content to send. - #[ruma_api(body)] - pub body: Raw, - } - - response: { - /// A unique identifier for the event. - pub event_id: Box, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room id, transaction id and event content. - /// - /// # Errors - /// - /// Since `Request` stores the request body in serialized form, this function can fail if `T`s - /// [`Serialize`][serde::Serialize] implementation can fail. - pub fn new( - room_id: &'a RoomId, - txn_id: &'a TransactionId, - content: &'a T, - ) -> serde_json::Result { - Ok(Self { - room_id, - txn_id, - event_type: content.event_type(), - body: Raw::from_json(to_raw_json_value(content)?), - }) - } - - /// Creates a new `Request` with the given room id, transaction id, event type and raw event - /// content. - pub fn new_raw( - room_id: &'a RoomId, - txn_id: &'a TransactionId, - event_type: &'a str, - body: Raw, - ) -> Self { - Self { room_id, event_type, txn_id, body } - } -} - -impl Response { - /// Creates a new `Response` with the given event id. - pub fn new(event_id: Box) -> Self { - Self { event_id } - } -} diff --git a/crates/ruma-client-api/src/r0/presence/get_presence.rs b/crates/ruma-client-api/src/r0/presence/get_presence.rs deleted file mode 100644 index a1ac53b7..00000000 --- a/crates/ruma-client-api/src/r0/presence/get_presence.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! [GET /_matrix/client/r0/presence/{userId}/status](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-presence-userid-status) - -use std::time::Duration; - -use ruma_api::ruma_api; -use ruma_common::presence::PresenceState; -use ruma_identifiers::UserId; - -ruma_api! { - metadata: { - description: "Get presence status for this user.", - method: GET, - name: "get_presence", - r0_path: "/_matrix/client/r0/presence/:user_id/status", - stable_path: "/_matrix/client/v3/presence/:user_id/status", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The user whose presence state will be retrieved. - #[ruma_api(path)] - pub user_id: &'a UserId, - } - - response: { - /// The state message for this user if one was set. - #[serde(skip_serializing_if = "Option::is_none")] - pub status_msg: Option, - - /// Whether or not the user is currently active. - #[serde(skip_serializing_if = "Option::is_none")] - pub currently_active: Option, - - /// The length of time in milliseconds since an action was performed by the user. - #[serde( - with = "ruma_serde::duration::opt_ms", - default, - skip_serializing_if = "Option::is_none", - )] - pub last_active_ago: Option, - - /// The user's presence state. - pub presence: PresenceState, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID. - pub fn new(user_id: &'a UserId) -> Self { - Self { user_id } - } -} - -impl Response { - /// Creates a new `Response` with the given presence state. - pub fn new(presence: PresenceState) -> Self { - Self { presence, status_msg: None, currently_active: None, last_active_ago: None } - } -} diff --git a/crates/ruma-client-api/src/r0/presence/set_presence.rs b/crates/ruma-client-api/src/r0/presence/set_presence.rs deleted file mode 100644 index 09d457ee..00000000 --- a/crates/ruma-client-api/src/r0/presence/set_presence.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! [PUT /_matrix/client/r0/presence/{userId}/status](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-presence-userid-status) - -use ruma_api::ruma_api; -use ruma_common::presence::PresenceState; -use ruma_identifiers::UserId; - -ruma_api! { - metadata: { - description: "Set presence status for this user.", - method: PUT, - name: "set_presence", - r0_path: "/_matrix/client/r0/presence/:user_id/status", - stable_path: "/_matrix/client/v3/presence/:user_id/status", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The user whose presence state will be updated. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// The new presence state. - pub presence: PresenceState, - - /// The status message to attach to this state. - #[serde(skip_serializing_if = "Option::is_none")] - pub status_msg: Option<&'a str>, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID and presence state. - pub fn new(user_id: &'a UserId, presence: PresenceState) -> Self { - Self { user_id, presence, status_msg: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/profile/get_avatar_url.rs b/crates/ruma-client-api/src/r0/profile/get_avatar_url.rs deleted file mode 100644 index 1baea0d3..00000000 --- a/crates/ruma-client-api/src/r0/profile/get_avatar_url.rs +++ /dev/null @@ -1,65 +0,0 @@ -//! [GET /_matrix/client/r0/profile/{userId}/avatar_url](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-profile-userid-avatar-url) - -use ruma_api::ruma_api; -use ruma_identifiers::{MxcUri, UserId}; - -ruma_api! { - metadata: { - description: "Get the avatar URL of a user.", - method: GET, - name: "get_avatar_url", - r0_path: "/_matrix/client/r0/profile/:user_id/avatar_url", - stable_path: "/_matrix/client/v3/profile/:user_id/avatar_url", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// The user whose avatar URL will be retrieved. - #[ruma_api(path)] - pub user_id: &'a UserId, - } - - #[derive(Default)] - response: { - /// The user's avatar URL, if set. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will result - /// in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub avatar_url: Option>, - - /// The [BlurHash](https://blurha.sh) for the avatar pointed to by `avatar_url`. - /// - /// This uses the unstable prefix in - /// [MSC2448](https://github.com/matrix-org/matrix-doc/pull/2448). - #[cfg(feature = "unstable-msc2448")] - #[serde(rename = "xyz.amorgan.blurhash", skip_serializing_if = "Option::is_none")] - pub blurhash: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID. - pub fn new(user_id: &'a UserId) -> Self { - Self { user_id } - } -} - -impl Response { - /// Creates a new `Response` with the given avatar URL. - pub fn new(avatar_url: Option>) -> Self { - Self { - avatar_url, - #[cfg(feature = "unstable-msc2448")] - blurhash: None, - } - } -} diff --git a/crates/ruma-client-api/src/r0/profile/get_display_name.rs b/crates/ruma-client-api/src/r0/profile/get_display_name.rs deleted file mode 100644 index c521dfd2..00000000 --- a/crates/ruma-client-api/src/r0/profile/get_display_name.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! [GET /_matrix/client/r0/profile/{userId}/displayname](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-profile-userid-displayname) - -use ruma_api::ruma_api; -use ruma_identifiers::UserId; - -ruma_api! { - metadata: { - description: "Get the display name of a user.", - method: GET, - name: "get_display_name", - r0_path: "/_matrix/client/r0/profile/:user_id/displayname", - stable_path: "/_matrix/client/v3/profile/:user_id/displayname", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// The user whose display name will be retrieved. - #[ruma_api(path)] - pub user_id: &'a UserId, - } - - #[derive(Default)] - response: { - /// The user's display name, if set. - #[serde(skip_serializing_if = "Option::is_none")] - pub displayname: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID. - pub fn new(user_id: &'a UserId) -> Self { - Self { user_id } - } -} - -impl Response { - /// Creates a new `Response` with the given display name. - pub fn new(displayname: Option) -> Self { - Self { displayname } - } -} diff --git a/crates/ruma-client-api/src/r0/profile/get_profile.rs b/crates/ruma-client-api/src/r0/profile/get_profile.rs deleted file mode 100644 index 8adc30d1..00000000 --- a/crates/ruma-client-api/src/r0/profile/get_profile.rs +++ /dev/null @@ -1,70 +0,0 @@ -//! [GET /_matrix/client/r0/profile/{userId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-profile-userid) - -use ruma_api::ruma_api; -use ruma_identifiers::{MxcUri, UserId}; - -ruma_api! { - metadata: { - description: "Get all profile information of an user.", - method: GET, - name: "get_profile", - r0_path: "/_matrix/client/r0/profile/:user_id", - stable_path: "/_matrix/client/v3/profile/:user_id", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// The user whose profile will be retrieved. - #[ruma_api(path)] - pub user_id: &'a UserId, - } - - #[derive(Default)] - response: { - /// The user's avatar URL, if set. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will result - /// in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub avatar_url: Option>, - - /// The user's display name, if set. - #[serde(skip_serializing_if = "Option::is_none")] - pub displayname: Option, - - /// The [BlurHash](https://blurha.sh) for the avatar pointed to by `avatar_url`. - /// - /// This uses the unstable prefix in - /// [MSC2448](https://github.com/matrix-org/matrix-doc/pull/2448). - #[cfg(feature = "unstable-msc2448")] - #[serde(rename = "xyz.amorgan.blurhash", skip_serializing_if = "Option::is_none")] - pub blurhash: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID. - pub fn new(user_id: &'a UserId) -> Self { - Self { user_id } - } -} - -impl Response { - /// Creates a new `Response` with the given avatar URL and display name. - pub fn new(avatar_url: Option>, displayname: Option) -> Self { - Self { - avatar_url, - displayname, - #[cfg(feature = "unstable-msc2448")] - blurhash: None, - } - } -} diff --git a/crates/ruma-client-api/src/r0/profile/set_avatar_url.rs b/crates/ruma-client-api/src/r0/profile/set_avatar_url.rs deleted file mode 100644 index 67fa3977..00000000 --- a/crates/ruma-client-api/src/r0/profile/set_avatar_url.rs +++ /dev/null @@ -1,110 +0,0 @@ -//! [PUT /_matrix/client/r0/profile/{userId}/avatar_url](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-profile-userid-avatar-url) - -use ruma_api::ruma_api; -use ruma_identifiers::{MxcUri, UserId}; - -ruma_api! { - metadata: { - description: "Set the avatar URL of the user.", - method: PUT, - name: "set_avatar_url", - r0_path: "/_matrix/client/r0/profile/:user_id/avatar_url", - stable_path: "/_matrix/client/v3/profile/:user_id/avatar_url", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The user whose avatar URL will be set. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// The new avatar URL for the user. - /// - /// `None` is used to unset the avatar. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will result - /// in `None` here during deserialization. - #[cfg_attr( - feature = "compat", - serde( - default, - deserialize_with = "ruma_serde::empty_string_as_none", - serialize_with = "ruma_serde::none_as_empty_string" - ) - )] - #[cfg_attr( - not(feature = "compat"), - serde(skip_serializing_if = "Option::is_none") - )] - pub avatar_url: Option<&'a MxcUri>, - - /// The [BlurHash](https://blurha.sh) for the avatar pointed to by `avatar_url`. - /// - /// This uses the unstable prefix in - /// [MSC2448](https://github.com/matrix-org/matrix-doc/pull/2448). - #[cfg(feature = "unstable-msc2448")] - #[serde(rename = "xyz.amorgan.blurhash", skip_serializing_if = "Option::is_none")] - pub blurhash: Option<&'a str>, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID and avatar URL. - pub fn new(user_id: &'a UserId, avatar_url: Option<&'a MxcUri>) -> Self { - Self { - user_id, - avatar_url, - #[cfg(feature = "unstable-msc2448")] - blurhash: None, - } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} - -#[cfg(all(test, feature = "server"))] -mod tests { - use matches::assert_matches; - use ruma_api::IncomingRequest as _; - - use super::IncomingRequest; - - #[test] - fn deserialize_unset_request() { - assert_matches!( - IncomingRequest::try_from_http_request( - http::Request::builder() - .method("PUT") - .uri("https://bar.org/_matrix/client/r0/profile/@foo:bar.org/avatar_url") - .body(&[] as &[u8]).unwrap(), - &["@foo:bar.org"], - ).unwrap(), - IncomingRequest { user_id, avatar_url: None, .. } if user_id == "@foo:bar.org" - ); - - #[cfg(feature = "compat")] - assert_matches!( - IncomingRequest::try_from_http_request( - http::Request::builder() - .method("PUT") - .uri("https://bar.org/_matrix/client/r0/profile/@foo:bar.org/avatar_url") - .body(serde_json::to_vec(&serde_json::json!({ "avatar_url": "" })).unwrap()) - .unwrap(), - &["@foo:bar.org"], - ).unwrap(), - IncomingRequest { user_id, avatar_url: None, .. } if user_id == "@foo:bar.org" - ); - } -} diff --git a/crates/ruma-client-api/src/r0/profile/set_display_name.rs b/crates/ruma-client-api/src/r0/profile/set_display_name.rs deleted file mode 100644 index 5bbb7c8f..00000000 --- a/crates/ruma-client-api/src/r0/profile/set_display_name.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! [PUT /_matrix/client/r0/profile/{userId}/displayname](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-profile-userid-displayname) - -use ruma_api::ruma_api; -use ruma_identifiers::UserId; - -ruma_api! { - metadata: { - description: "Set the display name of the user.", - method: PUT, - name: "set_display_name", - r0_path: "/_matrix/client/r0/profile/:user_id/displayname", - stable_path: "/_matrix/client/v3/profile/:user_id/displayname", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The user whose display name will be set. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// The new display name for the user. - #[serde(skip_serializing_if = "Option::is_none")] - pub displayname: Option<&'a str>, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID and display name. - pub fn new(user_id: &'a UserId, displayname: Option<&'a str>) -> Self { - Self { user_id, displayname } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/push/delete_pushrule.rs b/crates/ruma-client-api/src/r0/push/delete_pushrule.rs deleted file mode 100644 index 48e1214a..00000000 --- a/crates/ruma-client-api/src/r0/push/delete_pushrule.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! [DELETE /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}](https://matrix.org/docs/spec/client_server/r0.6.1#delete-matrix-client-r0-pushrules-scope-kind-ruleid) - -use ruma_api::ruma_api; - -use super::RuleKind; - -ruma_api! { - metadata: { - description: "This endpoint removes the push rule defined in the path.", - method: DELETE, - name: "delete_pushrule", - r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id", - stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The scope to delete from. 'global' to specify global rules. - #[ruma_api(path)] - pub scope: &'a str, - - /// The kind of rule - #[ruma_api(path)] - pub kind: RuleKind, - - /// The identifier for the rule. - #[ruma_api(path)] - pub rule_id: &'a str, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given scope, kind and rule ID. - pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { - Self { scope, kind, rule_id } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/push/get_notifications.rs b/crates/ruma-client-api/src/r0/push/get_notifications.rs deleted file mode 100644 index 6babaa68..00000000 --- a/crates/ruma-client-api/src/r0/push/get_notifications.rs +++ /dev/null @@ -1,111 +0,0 @@ -//! [GET /_matrix/client/r0/notifications](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-notifications) - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_common::{push::Action, MilliSecondsSinceUnixEpoch}; -use ruma_events::AnySyncRoomEvent; -use ruma_identifiers::RoomId; -use ruma_serde::Raw; -use serde::{Deserialize, Serialize}; - -ruma_api! { - metadata: { - description: "Paginate through the list of events that the user has been, or would have been notified about.", - method: GET, - name: "get_notifications", - r0_path: "/_matrix/client/r0/notifications", - stable_path: "/_matrix/client/v3/notifications", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: { - /// Pagination token given to retrieve the next set of events. - #[ruma_api(query)] - #[serde(skip_serializing_if = "Option::is_none")] - pub from: Option<&'a str>, - - /// Limit on the number of events to return in this request. - #[ruma_api(query)] - #[serde(skip_serializing_if = "Option::is_none")] - pub limit: Option, - - /// Allows basic filtering of events returned. - /// - /// Supply "highlight" to return only events where the notification had the 'highlight' - /// tweak set. - #[ruma_api(query)] - #[serde(skip_serializing_if = "Option::is_none")] - pub only: Option<&'a str>, - } - - response: { - /// The token to supply in the from param of the next /notifications request in order to - /// request more events. - /// - /// If this is absent, there are no more results. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_token: Option, - - - /// The list of events that triggered notifications. - pub notifications: Vec, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates an empty `Request`. - pub fn new() -> Self { - Default::default() - } -} - -impl Response { - /// Creates a new `Response` with the given notifications. - pub fn new(notifications: Vec) -> Self { - Self { next_token: None, notifications } - } -} - -/// Represents a notification. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Notification { - /// The actions to perform when the conditions for this rule are met. - pub actions: Vec, - - /// The event that triggered the notification. - pub event: Raw, - - /// The profile tag of the rule that matched this event. - #[serde(skip_serializing_if = "Option::is_none")] - pub profile_tag: Option, - - /// Indicates whether the user has sent a read receipt indicating that they have read this - /// message. - pub read: bool, - - /// The ID of the room in which the event was posted. - pub room_id: Box, - - /// The time at which the event notification was sent. - pub ts: MilliSecondsSinceUnixEpoch, -} - -impl Notification { - /// Creates a new `Notification` with the given actions, event, read flag, room ID and - /// timestamp. - pub fn new( - actions: Vec, - event: Raw, - read: bool, - room_id: Box, - ts: MilliSecondsSinceUnixEpoch, - ) -> Self { - Self { actions, event, profile_tag: None, read, room_id, ts } - } -} diff --git a/crates/ruma-client-api/src/r0/push/get_pushers.rs b/crates/ruma-client-api/src/r0/push/get_pushers.rs deleted file mode 100644 index 964b07d2..00000000 --- a/crates/ruma-client-api/src/r0/push/get_pushers.rs +++ /dev/null @@ -1,141 +0,0 @@ -//! [GET /_matrix/client/r0/pushers](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-pushers) - -use ruma_api::ruma_api; -use serde::{Deserialize, Serialize}; - -use super::{PusherData, PusherKind}; - -ruma_api! { - metadata: { - description: "Gets all currently active pushers for the authenticated user.", - method: GET, - name: "get_pushers", - r0_path: "/_matrix/client/r0/pushers", - stable_path: "/_matrix/client/v3/pushers", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// An array containing the current pushers for the user. - pub pushers: Vec, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given pushers. - pub fn new(pushers: Vec) -> Self { - Self { pushers } - } -} - -/// Defines a pusher. -/// -/// To create an instance of this type, first create a `PusherInit` and convert it via -/// `Pusher::from` / `.into()`. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Pusher { - /// A unique identifier for this pusher. - /// - /// The maximum allowed length is 512 bytes. - pub pushkey: String, - - /// The kind of the pusher. - pub kind: PusherKind, - - /// A reverse-DNS style identifier for the application. - /// - /// The maximum allowed length is 64 bytes. - pub app_id: String, - - /// A string that will allow the user to identify what application owns this pusher. - pub app_display_name: String, - - /// A string that will allow the user to identify what device owns this pusher. - pub device_display_name: String, - - /// Determines which set of device specific rules this pusher executes. - #[serde(skip_serializing_if = "Option::is_none")] - pub profile_tag: Option, - - /// The preferred language for receiving notifications (e.g. 'en' or 'en-US') - pub lang: String, - - /// Information for the pusher implementation itself. - pub data: PusherData, -} - -/// Initial set of fields of `Pusher`. -/// -/// This struct will not be updated even if additional fields are added to `Pusher` in a new -/// (non-breaking) release of the Matrix specification. -#[derive(Debug)] -#[allow(clippy::exhaustive_structs)] -pub struct PusherInit { - /// A unique identifier for this pusher. - /// - /// The maximum allowed length is 512 bytes. - pub pushkey: String, - - /// The kind of the pusher. - pub kind: PusherKind, - - /// A reverse-DNS style identifier for the application. - /// - /// The maximum allowed length is 64 bytes. - pub app_id: String, - - /// A string that will allow the user to identify what application owns this pusher. - pub app_display_name: String, - - /// A string that will allow the user to identify what device owns this pusher. - pub device_display_name: String, - - /// Determines which set of device-specific rules this pusher executes. - pub profile_tag: Option, - - /// The preferred language for receiving notifications (e.g. 'en' or 'en-US'). - pub lang: String, - - /// Information for the pusher implementation itself. - pub data: PusherData, -} - -impl From for Pusher { - fn from(init: PusherInit) -> Self { - let PusherInit { - pushkey, - kind, - app_id, - app_display_name, - device_display_name, - profile_tag, - lang, - data, - } = init; - Self { - pushkey, - kind, - app_id, - app_display_name, - device_display_name, - profile_tag, - lang, - data, - } - } -} diff --git a/crates/ruma-client-api/src/r0/push/get_pushrule.rs b/crates/ruma-client-api/src/r0/push/get_pushrule.rs deleted file mode 100644 index 5ee8fb9c..00000000 --- a/crates/ruma-client-api/src/r0/push/get_pushrule.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! [GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-pushrules-scope-kind-ruleid) - -use ruma_api::ruma_api; - -use super::{PushRule, RuleKind}; - -ruma_api! { - metadata: { - description: "Retrieve a single specified push rule.", - method: GET, - name: "get_pushrule", - r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id", - stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The scope to fetch rules from. 'global' to specify global rules. - #[ruma_api(path)] - pub scope: &'a str, - - /// The kind of rule. - #[ruma_api(path)] - pub kind: RuleKind, - - /// The identifier for the rule. - #[ruma_api(path)] - pub rule_id: &'a str, - } - - response: { - /// The specific push rule. - #[ruma_api(body)] - pub rule: PushRule, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given scope, rule kind and rule ID. - pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { - Self { scope, kind, rule_id } - } -} - -impl Response { - /// Creates a new `Response` with the given rule. - pub fn new(rule: PushRule) -> Self { - Self { rule } - } -} diff --git a/crates/ruma-client-api/src/r0/push/get_pushrule_actions.rs b/crates/ruma-client-api/src/r0/push/get_pushrule_actions.rs deleted file mode 100644 index 052ab02c..00000000 --- a/crates/ruma-client-api/src/r0/push/get_pushrule_actions.rs +++ /dev/null @@ -1,54 +0,0 @@ -//! [GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-pushrules-scope-kind-ruleid-actions) - -use ruma_api::ruma_api; -use ruma_common::push::Action; - -use super::RuleKind; - -ruma_api! { - metadata: { - description: "This endpoint get the actions for the specified push rule.", - method: GET, - name: "get_pushrule_actions", - r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id/actions", - stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id/actions", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The scope to fetch a rule from. 'global' to specify global rules. - #[ruma_api(path)] - pub scope: &'a str, - - /// The kind of rule - #[ruma_api(path)] - pub kind: RuleKind, - - /// The identifier for the rule. - #[ruma_api(path)] - pub rule_id: &'a str, - } - - response: { - /// The actions to perform for this rule. - pub actions: Vec, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given scope, kind and rule ID. - pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { - Self { scope, kind, rule_id } - } -} - -impl Response { - /// Creates a new `Response` with the given actions. - pub fn new(actions: Vec) -> Self { - Self { actions } - } -} diff --git a/crates/ruma-client-api/src/r0/push/get_pushrule_enabled.rs b/crates/ruma-client-api/src/r0/push/get_pushrule_enabled.rs deleted file mode 100644 index 19880ee8..00000000 --- a/crates/ruma-client-api/src/r0/push/get_pushrule_enabled.rs +++ /dev/null @@ -1,53 +0,0 @@ -//! [GET /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-pushrules-scope-kind-ruleid-enabled) - -use ruma_api::ruma_api; - -use super::RuleKind; - -ruma_api! { - metadata: { - description: "This endpoint gets whether the specified push rule is enabled.", - method: GET, - name: "get_pushrule_enabled", - r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id/enabled", - stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id/enabled", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The scope to fetch a rule from. 'global' to specify global rules. - #[ruma_api(path)] - pub scope: &'a str, - - /// The kind of rule - #[ruma_api(path)] - pub kind: RuleKind, - - /// The identifier for the rule. - #[ruma_api(path)] - pub rule_id: &'a str, - } - - response: { - /// Whether the push rule is enabled or not. - pub enabled: bool, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given scope, rule kind and rule ID. - pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { - Self { scope, kind, rule_id } - } -} - -impl Response { - /// Creates a new `Response` with the given enabled flag. - pub fn new(enabled: bool) -> Self { - Self { enabled } - } -} diff --git a/crates/ruma-client-api/src/r0/push/get_pushrules_all.rs b/crates/ruma-client-api/src/r0/push/get_pushrules_all.rs deleted file mode 100644 index 3d414350..00000000 --- a/crates/ruma-client-api/src/r0/push/get_pushrules_all.rs +++ /dev/null @@ -1,41 +0,0 @@ -//! [GET /_matrix/client/r0/pushrules/](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-pushrules) - -use ruma_api::ruma_api; -use ruma_common::push::Ruleset; - -ruma_api! { - metadata: { - description: "Retrieve all push rulesets for this user.", - method: GET, - name: "get_pushrules_all", - r0_path: "/_matrix/client/r0/pushrules/", - stable_path: "/_matrix/client/v3/pushrules/", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// The global ruleset. - pub global: Ruleset, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given global ruleset. - pub fn new(global: Ruleset) -> Self { - Self { global } - } -} diff --git a/crates/ruma-client-api/src/r0/push/get_pushrules_global_scope.rs b/crates/ruma-client-api/src/r0/push/get_pushrules_global_scope.rs deleted file mode 100644 index 1cecd8be..00000000 --- a/crates/ruma-client-api/src/r0/push/get_pushrules_global_scope.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! [GET /_matrix/client/r0/pushrules/global/](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-pushrules) - -use ruma_api::ruma_api; -use ruma_common::push::Ruleset; - -ruma_api! { - metadata: { - description: "Retrieve all push rulesets in the global scope for this user.", - method: GET, - name: "get_pushrules_global_scope", - r0_path: "/_matrix/client/r0/pushrules/global/", - stable_path: "/_matrix/client/v3/pushrules/global/", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// The global ruleset. - #[ruma_api(body)] - pub global: Ruleset, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given global ruleset. - pub fn new(global: Ruleset) -> Self { - Self { global } - } -} diff --git a/crates/ruma-client-api/src/r0/push/set_pusher.rs b/crates/ruma-client-api/src/r0/push/set_pusher.rs deleted file mode 100644 index 60e66809..00000000 --- a/crates/ruma-client-api/src/r0/push/set_pusher.rs +++ /dev/null @@ -1,152 +0,0 @@ -//! [POST /_matrix/client/r0/pushers/set](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-pushers-set) - -use ruma_api::ruma_api; -use serde::{Deserialize, Serialize}; - -use super::{PusherData, PusherKind}; - -ruma_api! { - metadata: { - description: "This endpoint allows the creation, modification and deletion of pushers for this user ID.", - method: POST, - name: "set_pusher", - r0_path: "/_matrix/client/r0/pushers/set", - stable_path: "/_matrix/client/v3/pushers/set", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The pusher to configure. - #[serde(flatten)] - pub pusher: Pusher, - - /// Controls if another pusher with the same pushkey and app id should be created. - /// - /// Defaults to `false`. See the spec for more details. - #[serde(default, skip_serializing_if = "ruma_serde::is_default")] - pub append: bool, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl Request { - /// Creates a new `Request` with the given pusher. - pub fn new(pusher: Pusher) -> Self { - Self { pusher, append: false } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} - -/// Defines a pusher. -/// -/// To create an instance of this type, first create a `PusherInit` and convert it via -/// `Pusher::from` / `.into()`. -#[derive(Clone, Debug, Serialize, Deserialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Pusher { - /// A unique identifier for this pusher. - /// - /// The maximum allowed length is 512 bytes. - pub pushkey: String, - - /// The kind of the pusher. - /// - /// `None` deletes the pusher. - pub kind: Option, - - /// A reverse-DNS style identifier for the application. - /// - /// The maximum allowed length is 64 bytes. - pub app_id: String, - - /// A string that will allow the user to identify what application owns this pusher. - pub app_display_name: String, - - /// A string that will allow the user to identify what device owns this pusher. - pub device_display_name: String, - - /// Determines which set of device specific rules this pusher executes. - #[serde(skip_serializing_if = "Option::is_none")] - pub profile_tag: Option, - - /// The preferred language for receiving notifications (e.g. 'en' or 'en-US') - pub lang: String, - - /// Information for the pusher implementation itself. - pub data: PusherData, -} - -/// Initial set of fields of `Pusher`. -/// -/// This struct will not be updated even if additional fields are added to `Pusher` in a new -/// (non-breaking) release of the Matrix specification. -#[derive(Debug)] -#[allow(clippy::exhaustive_structs)] -pub struct PusherInit { - /// A unique identifier for this pusher. - /// - /// The maximum allowed length is 512 bytes. - pub pushkey: String, - - /// The kind of the pusher. - /// - /// `None` deletes the pusher. - pub kind: Option, - - /// A reverse-DNS style identifier for the application. - /// - /// The maximum allowed length is 64 bytes. - pub app_id: String, - - /// A string that will allow the user to identify what application owns this pusher. - pub app_display_name: String, - - /// A string that will allow the user to identify what device owns this pusher. - pub device_display_name: String, - - /// Determines which set of device specific rules this pusher executes. - pub profile_tag: Option, - - /// The preferred language for receiving notifications (e.g. 'en' or 'en-US') - pub lang: String, - - /// Information for the pusher implementation itself. - pub data: PusherData, -} - -impl From for Pusher { - fn from(init: PusherInit) -> Self { - let PusherInit { - pushkey, - kind, - app_id, - app_display_name, - device_display_name, - profile_tag, - lang, - data, - } = init; - Self { - pushkey, - kind, - app_id, - app_display_name, - device_display_name, - profile_tag, - lang, - data, - } - } -} diff --git a/crates/ruma-client-api/src/r0/push/set_pushrule.rs b/crates/ruma-client-api/src/r0/push/set_pushrule.rs deleted file mode 100644 index 94f9c85c..00000000 --- a/crates/ruma-client-api/src/r0/push/set_pushrule.rs +++ /dev/null @@ -1,88 +0,0 @@ -//! [PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-pushrules-scope-kind-ruleid) - -use ruma_api::ruma_api; -use ruma_common::push::{Action, PushCondition}; - -use super::RuleKind; - -ruma_api! { - metadata: { - description: "This endpoint allows the creation, modification and deletion of pushers for this user ID.", - method: PUT, - name: "set_pushrule", - r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id", - stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The scope to set the rule in. 'global' to specify global rules. - #[ruma_api(path)] - pub scope: &'a str, - - /// The kind of rule - #[ruma_api(path)] - pub kind: RuleKind, - - /// The identifier for the rule. - #[ruma_api(path)] - pub rule_id: &'a str, - - /// Use 'before' with a rule_id as its value to make the new rule the next-most important - /// rule with respect to the given user defined rule. - #[ruma_api(query)] - pub before: Option<&'a str>, - - /// This makes the new rule the next-less important rule relative to the given user defined - /// rule. - #[ruma_api(query)] - pub after: Option<&'a str>, - - /// The actions to perform when this rule is matched. - pub actions: &'a [Action], - - /// The conditions that must hold true for an event in order for a rule to be applied to an - /// event. - /// - /// A rule with no conditions always matches. Only applicable to underride and override - /// rules, empty Vec otherwise. - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub conditions: &'a [PushCondition], - - /// The glob-style pattern to match against. - /// - /// Only applicable to content rules. - #[serde(skip_serializing_if = "Option::is_none")] - pub pattern: Option<&'a str>, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given scope, rule kind, rule ID and actions. - pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str, actions: &'a [Action]) -> Self { - Self { - scope, - kind, - rule_id, - before: None, - after: None, - actions, - conditions: &[], - pattern: None, - } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/push/set_pushrule_actions.rs b/crates/ruma-client-api/src/r0/push/set_pushrule_actions.rs deleted file mode 100644 index 753a332c..00000000 --- a/crates/ruma-client-api/src/r0/push/set_pushrule_actions.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! [PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/actions](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-pushrules-scope-kind-ruleid-actions) - -use ruma_api::ruma_api; -use ruma_common::push::Action; - -use super::RuleKind; - -ruma_api! { - metadata: { - description: "This endpoint allows clients to change the actions of a push rule. This can be used to change the actions of builtin rules.", - method: PUT, - name: "set_pushrule_actions", - r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id/actions", - stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id/actions", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The scope to fetch a rule from. 'global' to specify global rules. - #[ruma_api(path)] - pub scope: &'a str, - - /// The kind of rule - #[ruma_api(path)] - pub kind: RuleKind, - - /// The identifier for the rule. - #[ruma_api(path)] - pub rule_id: &'a str, - - /// The actions to perform for this rule - pub actions: Vec, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given scope, rule kind, rule ID and actions. - pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str, actions: Vec) -> Self { - Self { scope, kind, rule_id, actions } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/push/set_pushrule_enabled.rs b/crates/ruma-client-api/src/r0/push/set_pushrule_enabled.rs deleted file mode 100644 index 7248ac2b..00000000 --- a/crates/ruma-client-api/src/r0/push/set_pushrule_enabled.rs +++ /dev/null @@ -1,64 +0,0 @@ -//! [PUT /_matrix/client/r0/pushrules/{scope}/{kind}/{ruleId}/enabled](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-pushrules-scope-kind-ruleid-enabled) - -use ruma_api::ruma_api; - -use super::RuleKind; - -ruma_api! { - metadata: { - description: "This endpoint allows clients to enable or disable the specified push rule.", - method: PUT, - name: "set_pushrule_enabled", - r0_path: "/_matrix/client/r0/pushrules/:scope/:kind/:rule_id/enabled", - stable_path: "/_matrix/client/v3/pushrules/:scope/:kind/:rule_id/enabled", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The scope to fetch a rule from. 'global' to specify global rules. - #[ruma_api(path)] - pub scope: &'a str, - - /// The kind of rule - #[ruma_api(path)] - pub kind: RuleKind, - - /// The identifier for the rule. - #[ruma_api(path)] - pub rule_id: &'a str, - - /// Whether the push rule is enabled or not. - pub enabled: bool, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given scope, rule kind, rule ID and enabled flag. - pub fn new(scope: &'a str, kind: RuleKind, rule_id: &'a str, enabled: bool) -> Self { - Self { scope, kind, rule_id, enabled } - } - - /// Creates a new `Request` to enable the given rule. - pub fn enable(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { - Self::new(scope, kind, rule_id, true) - } - - /// Creates a new `Request` to disable the given rule. - pub fn disable(scope: &'a str, kind: RuleKind, rule_id: &'a str) -> Self { - Self::new(scope, kind, rule_id, false) - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/read_marker/set_read_marker.rs b/crates/ruma-client-api/src/r0/read_marker/set_read_marker.rs deleted file mode 100644 index 65c8f317..00000000 --- a/crates/ruma-client-api/src/r0/read_marker/set_read_marker.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/read_markers](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-read-markers) - -use ruma_api::ruma_api; -use ruma_identifiers::{EventId, RoomId}; - -ruma_api! { - metadata: { - description: "Sets the position of the read marker for a given room, and optionally the read receipt's location.", - method: POST, - name: "set_read_marker", - r0_path: "/_matrix/client/r0/rooms/:room_id/read_markers", - stable_path: "/_matrix/client/v3/rooms/:room_id/read_markers", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room ID to set the read marker in for the user. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The event ID the read marker should be located at. - /// - /// The event MUST belong to the room. - #[serde(rename = "m.fully_read")] - pub fully_read: &'a EventId, - - /// The event ID to set the read receipt location at. - /// - /// This is equivalent to calling the create_read_receipt endpoint and is provided here to - /// save that extra call. - #[serde(rename = "m.read", skip_serializing_if = "Option::is_none")] - pub read_receipt: Option<&'a EventId>, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID and fully read event ID. - pub fn new(room_id: &'a RoomId, fully_read: &'a EventId) -> Self { - Self { room_id, fully_read, read_receipt: None } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/receipt/create_receipt.rs b/crates/ruma-client-api/src/r0/receipt/create_receipt.rs deleted file mode 100644 index 030ed7b4..00000000 --- a/crates/ruma-client-api/src/r0/receipt/create_receipt.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/receipt/{receiptType}/{eventId}](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-receipt-receipttype-eventid) - -use ruma_api::ruma_api; -use ruma_common::receipt::ReceiptType; -use ruma_identifiers::{EventId, RoomId}; - -ruma_api! { - metadata: { - description: "Send a receipt event to a room.", - method: POST, - name: "create_receipt", - r0_path: "/_matrix/client/r0/rooms/:room_id/receipt/:receipt_type/:event_id", - stable_path: "/_matrix/client/v3/rooms/:room_id/receipt/:receipt_type/:event_id", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room in which to send the event. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The type of receipt to send. - #[ruma_api(path)] - pub receipt_type: ReceiptType, - - /// The event ID to acknowledge up to. - #[ruma_api(path)] - pub event_id: &'a EventId, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID, receipt type and event ID. - pub fn new(room_id: &'a RoomId, receipt_type: ReceiptType, event_id: &'a EventId) -> Self { - Self { room_id, receipt_type, event_id } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/redact/redact_event.rs b/crates/ruma-client-api/src/r0/redact/redact_event.rs deleted file mode 100644 index 558369b0..00000000 --- a/crates/ruma-client-api/src/r0/redact/redact_event.rs +++ /dev/null @@ -1,59 +0,0 @@ -//! [PUT /_matrix/client/r0/rooms/{roomId}/redact/{eventId}/{txnId}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-redact-eventid-txnid) - -use ruma_api::ruma_api; -use ruma_identifiers::{EventId, RoomId, TransactionId}; - -ruma_api! { - metadata: { - description: "Redact an event, stripping all information not critical to the event graph integrity.", - method: PUT, - name: "redact_event", - r0_path: "/_matrix/client/r0/rooms/:room_id/redact/:event_id/:txn_id", - stable_path: "/_matrix/client/v3/rooms/:room_id/redact/:event_id/:txn_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The ID of the room of the event to redact. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The ID of the event to redact. - #[ruma_api(path)] - pub event_id: &'a EventId, - - /// The transaction ID for this event. - /// - /// Clients should generate a unique ID; it will be used by the server to ensure idempotency - /// of requests. - #[ruma_api(path)] - pub txn_id: &'a TransactionId, - - /// The reason for the redaction. - #[serde(skip_serializing_if = "Option::is_none")] - pub reason: Option<&'a str>, - } - - response: { - /// The ID of the redacted event. - pub event_id: Box, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID, event ID and transaction ID. - pub fn new(room_id: &'a RoomId, event_id: &'a EventId, txn_id: &'a TransactionId) -> Self { - Self { room_id, event_id, txn_id, reason: None } - } -} - -impl Response { - /// Creates a new `Response` with the given event ID. - pub fn new(event_id: Box) -> Self { - Self { event_id } - } -} diff --git a/crates/ruma-client-api/src/r0/room/aliases.rs b/crates/ruma-client-api/src/r0/room/aliases.rs deleted file mode 100644 index 66d8cfb0..00000000 --- a/crates/ruma-client-api/src/r0/room/aliases.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! [GET /_matrix/client/r0/rooms/{roomId}/aliases](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-aliases) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomAliasId, RoomId}; - -ruma_api! { - metadata: { - description: "Get a list of aliases maintained by the local server for the given room.", - method: GET, - name: "aliases", - r0_path: "/_matrix/client/r0/rooms/:room_id/aliases", - stable_path: "/_matrix/client/v3/rooms/:room_id/aliases", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room ID to get aliases of. - #[ruma_api(path)] - pub room_id: &'a RoomId, - } - - response: { - /// The server's local aliases on the room. - pub aliases: Vec>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID. - pub fn new(room_id: &'a RoomId) -> Self { - Self { room_id } - } -} - -impl Response { - /// Creates a new `Response` with the given aliases. - pub fn new(aliases: Vec>) -> Self { - Self { aliases } - } -} diff --git a/crates/ruma-client-api/src/r0/room/create_room.rs b/crates/ruma-client-api/src/r0/room/create_room.rs deleted file mode 100644 index 05e8ae04..00000000 --- a/crates/ruma-client-api/src/r0/room/create_room.rs +++ /dev/null @@ -1,205 +0,0 @@ -//! [POST /_matrix/client/r0/createRoom](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-createroom) - -use assign::assign; -use ruma_api::ruma_api; -use ruma_events::{ - room::{ - create::{PreviousRoom, RoomCreateEventContent, RoomType}, - power_levels::RoomPowerLevelsEventContent, - }, - AnyInitialStateEvent, -}; -use ruma_identifiers::{RoomId, RoomName, RoomVersionId, UserId}; -use ruma_serde::{Raw, StringEnum}; -use serde::{Deserialize, Serialize}; - -use super::Visibility; -use crate::{ - r0::membership::{IncomingInvite3pid, Invite3pid}, - PrivOwnedStr, -}; - -ruma_api! { - metadata: { - description: "Create a new room.", - method: POST, - name: "create_room", - r0_path: "/_matrix/client/r0/createRoom", - stable_path: "/_matrix/client/v3/createRoom", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: { - /// Extra keys to be added to the content of the `m.room.create`. - #[serde(default, skip_serializing_if = "Option::is_none")] - pub creation_content: Option>, - - /// List of state events to send to the new room. - /// - /// Takes precedence over events set by preset, but gets overridden by name and topic keys. - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub initial_state: &'a [Raw], - - /// A list of user IDs to invite to the room. - /// - /// This will tell the server to invite everyone in the list to the newly created room. - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub invite: &'a [Box], - - /// List of third party IDs of users to invite. - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub invite_3pid: &'a [Invite3pid<'a>], - - /// If set, this sets the `is_direct` flag on room invites. - #[serde(default, skip_serializing_if = "ruma_serde::is_default")] - pub is_direct: bool, - - /// If this is included, an `m.room.name` event will be sent into the room to indicate the - /// name of the room. - #[serde(skip_serializing_if = "Option::is_none")] - pub name: Option<&'a RoomName>, - - /// Power level content to override in the default power level event. - #[serde(skip_serializing_if = "Option::is_none")] - pub power_level_content_override: Option>, - - /// Convenience parameter for setting various default state events based on a preset. - #[serde(skip_serializing_if = "Option::is_none")] - pub preset: Option, - - /// The desired room alias local part. - #[serde(skip_serializing_if = "Option::is_none")] - pub room_alias_name: Option<&'a str>, - - /// Room version to set for the room. - /// - /// Defaults to homeserver's default if not specified. - #[serde(skip_serializing_if = "Option::is_none")] - pub room_version: Option<&'a RoomVersionId>, - - /// If this is included, an `m.room.topic` event will be sent into the room to indicate - /// the topic for the room. - #[serde(skip_serializing_if = "Option::is_none")] - pub topic: Option<&'a str>, - - /// A public visibility indicates that the room will be shown in the published room list. - /// - /// A private visibility will hide the room from the published room list. Defaults to - /// `Private`. - #[serde(default, skip_serializing_if = "ruma_serde::is_default")] - pub visibility: Visibility, - } - - response: { - /// The created room's ID. - pub room_id: Box, - } - - error: crate::Error -} - -impl Request<'_> { - /// Creates a new `Request` will all-default parameters. - pub fn new() -> Self { - Default::default() - } -} - -impl Response { - /// Creates a new `Response` with the given room id. - pub fn new(room_id: Box) -> Self { - Self { room_id } - } -} - -/// Extra options to be added to the `m.room.create` event. -/// -/// This is the same as the event content struct for `m.room.create`, but without some fields that -/// servers are supposed to ignore. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct CreationContent { - /// Whether users on other servers can join this room. - /// - /// Defaults to `true` if key does not exist. - #[serde( - rename = "m.federate", - default = "ruma_serde::default_true", - skip_serializing_if = "ruma_serde::is_true" - )] - pub federate: bool, - - /// A reference to the room this room replaces, if the previous room was upgraded. - #[serde(skip_serializing_if = "Option::is_none")] - pub predecessor: Option, - - /// The room type. - /// - /// This is currently only used for spaces. - #[serde(skip_serializing_if = "Option::is_none", rename = "type")] - pub room_type: Option, -} - -impl CreationContent { - /// Creates a new `CreationContent` with all fields defaulted. - pub fn new() -> Self { - Self { federate: true, predecessor: None, room_type: None } - } - - /// Given a `CreationContent` and the other fields that a homeserver has to fill, construct - /// a `RoomCreateEventContent`. - pub fn into_event_content( - self, - creator: Box, - room_version: RoomVersionId, - ) -> RoomCreateEventContent { - assign!(RoomCreateEventContent::new(creator), { - federate: self.federate, - room_version: room_version, - predecessor: self.predecessor, - room_type: self.room_type - }) - } - - /// Returns whether all fields have their default value. - pub fn is_empty(&self) -> bool { - self.federate && self.predecessor.is_none() && self.room_type.is_none() - } -} - -impl Default for CreationContent { - fn default() -> Self { - Self::new() - } -} - -/// A convenience parameter for setting a few default state events. -/// -/// This type can hold an arbitrary string. To check for formats that are not available as a -/// documented variant here, use its string representation, obtained through `.as_str()`. -#[derive(Clone, Debug, PartialEq, Eq, StringEnum)] -#[ruma_enum(rename_all = "snake_case")] -#[non_exhaustive] -pub enum RoomPreset { - /// `join_rules` is set to `invite` and `history_visibility` is set to `shared`. - PrivateChat, - - /// `join_rules` is set to `public` and `history_visibility` is set to `shared`. - PublicChat, - - /// Same as `PrivateChat`, but all initial invitees get the same power level as the creator. - TrustedPrivateChat, - - #[doc(hidden)] - _Custom(PrivOwnedStr), -} - -impl RoomPreset { - /// Creates a string slice from this `RoomPreset`. - pub fn as_str(&self) -> &str { - self.as_ref() - } -} diff --git a/crates/ruma-client-api/src/r0/room/get_room_event.rs b/crates/ruma-client-api/src/r0/room/get_room_event.rs deleted file mode 100644 index 2375819a..00000000 --- a/crates/ruma-client-api/src/r0/room/get_room_event.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! [GET /_matrix/client/r0/rooms/{roomId}/event/{eventId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-event-eventid) - -use ruma_api::ruma_api; -use ruma_events::AnyRoomEvent; -use ruma_identifiers::{EventId, RoomId}; -use ruma_serde::Raw; - -ruma_api! { - metadata: { - description: "Get a single event based on roomId/eventId", - method: GET, - name: "get_room_event", - r0_path: "/_matrix/client/r0/rooms/:room_id/event/:event_id", - stable_path: "/_matrix/client/v3/rooms/:room_id/event/:event_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The ID of the room the event is in. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The ID of the event. - #[ruma_api(path)] - pub event_id: &'a EventId, - } - - response: { - /// Arbitrary JSON of the event body. - #[ruma_api(body)] - pub event: Raw, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID and event ID. - pub fn new(room_id: &'a RoomId, event_id: &'a EventId) -> Self { - Self { room_id, event_id } - } -} - -impl Response { - /// Creates a new `Response` with the given event. - pub fn new(event: Raw) -> Self { - Self { event } - } -} diff --git a/crates/ruma-client-api/src/r0/room/report_content.rs b/crates/ruma-client-api/src/r0/room/report_content.rs deleted file mode 100644 index a5b54271..00000000 --- a/crates/ruma-client-api/src/r0/room/report_content.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/report/{eventId}](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-report-eventid) - -use js_int::Int; -use ruma_api::ruma_api; -use ruma_identifiers::{EventId, RoomId}; - -ruma_api! { - metadata: { - description: "Report content as inappropriate.", - method: POST, - name: "report_content", - r0_path: "/_matrix/client/r0/rooms/:room_id/report/:event_id", - stable_path: "/_matrix/client/v3/rooms/:room_id/report/:event_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// Room in which the event to be reported is located. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// Event to report. - #[ruma_api(path)] - pub event_id: &'a EventId, - - /// Integer between -100 and 0 rating offensivness. - pub score: Int, - - /// Reason to report content. - /// - /// May be blank. - pub reason: &'a str, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID, event ID, score and reason. - pub fn new(room_id: &'a RoomId, event_id: &'a EventId, score: Int, reason: &'a str) -> Self { - Self { room_id, event_id, score, reason } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/room/upgrade_room.rs b/crates/ruma-client-api/src/r0/room/upgrade_room.rs deleted file mode 100644 index 727c2206..00000000 --- a/crates/ruma-client-api/src/r0/room/upgrade_room.rs +++ /dev/null @@ -1,47 +0,0 @@ -//! [POST /_matrix/client/r0/rooms/{roomId}/upgrade](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-rooms-roomid-upgrade) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomId, RoomVersionId}; - -ruma_api! { - metadata: { - description: "Upgrades a room to a particular version.", - method: POST, - name: "upgrade_room", - r0_path: "/_matrix/client/r0/rooms/:room_id/upgrade", - stable_path: "/_matrix/client/v3/rooms/:room_id/upgrade", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// ID of the room to be upgraded. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// New version for the room. - pub new_version: &'a RoomVersionId, - } - - response: { - /// ID of the new room. - pub replacement_room: Box, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID and new room version. - pub fn new(room_id: &'a RoomId, new_version: &'a RoomVersionId) -> Self { - Self { room_id, new_version } - } -} - -impl Response { - /// Creates a new `Response` with the given room ID. - pub fn new(replacement_room: Box) -> Self { - Self { replacement_room } - } -} diff --git a/crates/ruma-client-api/src/r0/search/search_events.rs b/crates/ruma-client-api/src/r0/search/search_events.rs deleted file mode 100644 index 1ade6eb1..00000000 --- a/crates/ruma-client-api/src/r0/search/search_events.rs +++ /dev/null @@ -1,510 +0,0 @@ -//! [POST /_matrix/client/r0/search](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-search) - -use std::collections::BTreeMap; - -use js_int::{uint, UInt}; -use ruma_api::ruma_api; -use ruma_events::{AnyRoomEvent, AnyStateEvent}; -use ruma_identifiers::{EventId, MxcUri, RoomId, UserId}; -use ruma_serde::{Outgoing, Raw, StringEnum}; -use serde::{Deserialize, Serialize}; - -use crate::{ - r0::filter::{IncomingRoomEventFilter, RoomEventFilter}, - PrivOwnedStr, -}; - -ruma_api! { - metadata: { - description: "Search events.", - method: POST, - name: "search", - r0_path: "/_matrix/client/r0/search", - stable_path: "/_matrix/client/v3/search", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The point to return events from. - /// - /// If given, this should be a `next_batch` result from a previous call to this endpoint. - #[ruma_api(query)] - pub next_batch: Option<&'a str>, - - /// Describes which categories to search in and their criteria. - pub search_categories: Categories<'a>, - } - - response: { - /// A grouping of search results by category. - pub search_categories: ResultCategories, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given categories. - pub fn new(search_categories: Categories<'a>) -> Self { - Self { next_batch: None, search_categories } - } -} - -impl Response { - /// Creates a new `Response` with the given search results. - pub fn new(search_categories: ResultCategories) -> Self { - Self { search_categories } - } -} - -/// Categories of events that can be searched for. -#[derive(Clone, Debug, Default, Outgoing, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Categories<'a> { - /// Criteria for searching room events. - #[serde(skip_serializing_if = "Option::is_none")] - pub room_events: Option>, -} - -impl Categories<'_> { - /// Creates an empty `Categories`. - pub fn new() -> Self { - Default::default() - } -} - -/// Criteria for searching a category of events. -#[derive(Clone, Debug, Outgoing, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Criteria<'a> { - /// The string to search events for. - pub search_term: &'a str, - - /// The keys to search for. - /// - /// Defaults to all keys. - #[serde(skip_serializing_if = "Option::is_none")] - pub keys: Option<&'a [SearchKeys]>, - - /// A `Filter` to apply to the search. - #[serde(skip_serializing_if = "RoomEventFilter::is_empty")] - pub filter: RoomEventFilter<'a>, - - /// The order in which to search for results. - #[serde(skip_serializing_if = "Option::is_none")] - pub order_by: Option, - - /// Configures whether any context for the events returned are included in the response. - #[serde(default, skip_serializing_if = "EventContext::is_default")] - pub event_context: EventContext, - - /// Requests the server return the current state for each room returned. - #[serde(skip_serializing_if = "Option::is_none")] - pub include_state: Option, - - /// Requests that the server partitions the result set based on the provided list of keys. - #[serde(default, skip_serializing_if = "Groupings::is_empty")] - pub groupings: Groupings<'a>, -} - -impl<'a> Criteria<'a> { - /// Creates a new `Criteria` with the given search term. - pub fn new(search_term: &'a str) -> Self { - Self { - search_term, - keys: None, - filter: RoomEventFilter::default(), - order_by: None, - event_context: Default::default(), - include_state: None, - groupings: Default::default(), - } - } -} - -/// Configures whether any context for the events returned are included in the response. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct EventContext { - /// How many events before the result are returned. - #[serde( - default = "default_event_context_limit", - skip_serializing_if = "is_default_event_context_limit" - )] - pub before_limit: UInt, - - /// How many events after the result are returned. - #[serde( - default = "default_event_context_limit", - skip_serializing_if = "is_default_event_context_limit" - )] - pub after_limit: UInt, - - /// Requests that the server returns the historic profile information for the users that - /// sent the events that were returned. - #[serde(default, skip_serializing_if = "ruma_serde::is_default")] - pub include_profile: bool, -} - -fn default_event_context_limit() -> UInt { - uint!(5) -} - -#[allow(clippy::trivially_copy_pass_by_ref)] -fn is_default_event_context_limit(val: &UInt) -> bool { - *val == default_event_context_limit() -} - -impl EventContext { - /// Creates an `EventContext` with all-default values. - pub fn new() -> Self { - Self { - before_limit: default_event_context_limit(), - after_limit: default_event_context_limit(), - include_profile: false, - } - } - - /// Returns whether all fields have their default value. - pub fn is_default(&self) -> bool { - self.before_limit == default_event_context_limit() - && self.after_limit == default_event_context_limit() - && !self.include_profile - } -} - -impl Default for EventContext { - fn default() -> Self { - Self::new() - } -} - -/// Context for search results, if requested. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct EventContextResult { - /// Pagination token for the end of the chunk. - #[serde(skip_serializing_if = "Option::is_none")] - pub end: Option, - - /// Events just after the result. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events_after: Vec>, - - /// Events just before the result. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events_before: Vec>, - - /// The historic profile information of the users that sent the events returned. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub profile_info: BTreeMap, UserProfile>, - - /// Pagination token for the start of the chunk. - #[serde(skip_serializing_if = "Option::is_none")] - pub start: Option, -} - -impl EventContextResult { - /// Creates an empty `EventContextResult`. - pub fn new() -> Self { - Default::default() - } - - /// Returns whether all fields are `None` or an empty list. - pub fn is_empty(&self) -> bool { - self.end.is_none() - && self.events_after.is_empty() - && self.events_before.is_empty() - && self.profile_info.is_empty() - && self.start.is_none() - } -} - -/// A grouping for partitioning the result set. -#[derive(Clone, Default, Debug, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Grouping { - /// The key within events to use for this grouping. - pub key: Option, -} - -impl Grouping { - /// Creates an empty `Grouping`. - pub fn new() -> Self { - Default::default() - } - - /// Returns whether `key` is `None`. - pub fn is_empty(&self) -> bool { - self.key.is_none() - } -} - -/// The key within events to use for this grouping. -/// -/// This type can hold an arbitrary string. To check for formats that are not available as a -/// documented variant here, use its string representation, obtained through `.as_str()`. -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, StringEnum)] -#[ruma_enum(rename_all = "snake_case")] -#[non_exhaustive] -pub enum GroupingKey { - /// `room_id` - RoomId, - - /// `sender` - Sender, - - #[doc(hidden)] - _Custom(PrivOwnedStr), -} - -impl GroupingKey { - /// Creates a string slice from this `GroupingKey`. - pub fn as_str(&self) -> &str { - self.as_ref() - } -} - -/// Requests that the server partitions the result set based on the provided list of keys. -#[derive(Clone, Default, Debug, Outgoing, Serialize)] -#[incoming_derive(Default)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Groupings<'a> { - /// List of groups to request. - #[serde(default, skip_serializing_if = "<[_]>::is_empty")] - pub group_by: &'a [Grouping], -} - -impl Groupings<'_> { - /// Creates an empty `Groupings`. - pub fn new() -> Self { - Default::default() - } - - /// Returns `true` if all fields are empty. - pub fn is_empty(&self) -> bool { - self.group_by.is_empty() - } -} - -/// The keys to search for. -/// -/// This type can hold an arbitrary string. To check for formats that are not available as a -/// documented variant here, use its string representation, obtained through `.as_str()`. -#[derive(Clone, Debug, PartialEq, Eq, StringEnum)] -#[non_exhaustive] -pub enum SearchKeys { - /// content.body - #[ruma_enum(rename = "content.body")] - ContentBody, - - /// content.name - #[ruma_enum(rename = "content.name")] - ContentName, - - /// content.topic - #[ruma_enum(rename = "content.topic")] - ContentTopic, - - #[doc(hidden)] - _Custom(PrivOwnedStr), -} - -impl SearchKeys { - /// Creates a string slice from this `SearchKeys`. - pub fn as_str(&self) -> &str { - self.as_ref() - } -} - -/// The order in which to search for results. -#[derive(Clone, Debug, PartialEq, Eq, StringEnum)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[ruma_enum(rename_all = "snake_case")] -pub enum OrderBy { - /// Prioritize recent events. - Recent, - - /// Prioritize events by a numerical ranking of how closely they matched the search - /// criteria. - Rank, - - #[doc(hidden)] - _Custom(PrivOwnedStr), -} - -/// Categories of events that can be searched for. -#[derive(Clone, Default, Debug, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct ResultCategories { - /// Room event results. - #[serde(default, skip_serializing_if = "ResultRoomEvents::is_empty")] - pub room_events: ResultRoomEvents, -} - -impl ResultCategories { - /// Creates an empty `ResultCategories`. - pub fn new() -> Self { - Default::default() - } -} - -/// Categories of events that can be searched for. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct ResultRoomEvents { - /// An approximate count of the total number of results found. - #[serde(skip_serializing_if = "Option::is_none")] - pub count: Option, - - /// Any groups that were requested. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub groups: BTreeMap, ResultGroup>>, - - /// Token that can be used to get the next batch of results, by passing as the `next_batch` - /// parameter to the next call. - /// - /// If this field is absent, there are no more results. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_batch: Option, - - /// List of results in the requested order. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub results: Vec, - - /// The current state for every room in the results. - /// - /// This is included if the request had the `include_state` key set with a value of `true`. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub state: BTreeMap, Vec>>, - - /// List of words which should be highlighted, useful for stemming which may - /// change the query terms. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub highlights: Vec, -} - -impl ResultRoomEvents { - /// Creates an empty `ResultRoomEvents`. - pub fn new() -> Self { - Default::default() - } - - /// Returns `true` if all fields are empty / `None`. - pub fn is_empty(&self) -> bool { - self.count.is_none() - && self.groups.is_empty() - && self.next_batch.is_none() - && self.results.is_empty() - && self.state.is_empty() - && self.highlights.is_empty() - } -} - -/// A grouping of results, if requested. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct ResultGroup { - /// Token that can be used to get the next batch of results in the group, by passing as the - /// `next_batch` parameter to the next call. - /// - /// If this field is absent, there are no more results in this group. - #[serde(skip_serializing_if = "Option::is_none")] - pub next_batch: Option, - - /// Key that can be used to order different groups. - #[serde(skip_serializing_if = "Option::is_none")] - pub order: Option, - - /// Which results are in this group. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub results: Vec>, -} - -impl ResultGroup { - /// Creates an empty `ResultGroup`. - pub fn new() -> Self { - Default::default() - } - - /// Returns `true` if all fields are empty / `None`. - pub fn is_empty(&self) -> bool { - self.next_batch.is_none() && self.order.is_none() && self.results.is_empty() - } -} - -/// A search result. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct SearchResult { - /// Context for result, if requested. - #[serde(skip_serializing_if = "EventContextResult::is_empty")] - pub context: EventContextResult, - - /// A number that describes how closely this result matches the search. - /// - /// Higher is closer. - #[serde(skip_serializing_if = "Option::is_none")] - pub rank: Option, - - /// The event that matched. - #[serde(skip_serializing_if = "Option::is_none")] - pub result: Option>, -} - -impl SearchResult { - /// Creates an empty `SearchResult`. - pub fn new() -> Self { - Default::default() - } - - /// Returns `true` if all fields are empty / `None`. - pub fn is_empty(&self) -> bool { - self.context.is_empty() && self.rank.is_none() && self.result.is_none() - } -} - -/// A user profile. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct UserProfile { - /// The user's avatar URL, if set. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will result - /// in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub avatar_url: Option>, - - /// The user's display name, if set. - #[serde(skip_serializing_if = "Option::is_none")] - pub displayname: Option, -} - -impl UserProfile { - /// Creates an empty `UserProfile`. - pub fn new() -> Self { - Default::default() - } - - /// Returns `true` if all fields are `None`. - pub fn is_empty(&self) -> bool { - self.avatar_url.is_none() && self.displayname.is_none() - } -} - -/// Represents either a room or user ID for returning grouped search results. -#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] -#[allow(clippy::exhaustive_enums)] -pub enum RoomIdOrUserId { - /// Represents a room ID. - RoomId(Box), - - /// Represents a user ID. - UserId(Box), -} diff --git a/crates/ruma-client-api/src/r0/server/get_user_info.rs b/crates/ruma-client-api/src/r0/server/get_user_info.rs deleted file mode 100644 index efa10c56..00000000 --- a/crates/ruma-client-api/src/r0/server/get_user_info.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! [GET /_matrix/client/r0/admin/whois/{userId}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-admin-whois-userid) - -use std::collections::BTreeMap; - -use ruma_api::ruma_api; -use ruma_common::MilliSecondsSinceUnixEpoch; -use ruma_identifiers::UserId; -use serde::{Deserialize, Serialize}; - -ruma_api! { - metadata: { - description: "Get information about a particular user.", - method: GET, - name: "get_user_info", - r0_path: "/_matrix/client/r0/admin/whois/:user_id", - stable_path: "/_matrix/client/v3/admin/whois/:user_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The user to look up. - #[ruma_api(path)] - pub user_id: &'a UserId, - } - - #[derive(Default)] - response: { - /// The Matrix user ID of the user. - #[serde(skip_serializing_if = "Option::is_none")] - pub user_id: Option>, - - /// A map of the user's device identifiers to information about that device. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub devices: BTreeMap, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user id. - pub fn new(user_id: &'a UserId) -> Self { - Self { user_id } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Default::default() - } -} - -/// Information about a user's device. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct DeviceInfo { - /// A list of user sessions on this device. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub sessions: Vec, -} - -impl DeviceInfo { - /// Create a new `DeviceInfo` with no sessions. - pub fn new() -> Self { - Self::default() - } -} - -/// Information about a user session. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct SessionInfo { - /// A list of connections in this session. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub connections: Vec, -} - -impl SessionInfo { - /// Create a new `SessionInfo` with no connections. - pub fn new() -> Self { - Self::default() - } -} - -/// Information about a connection in a user session. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct ConnectionInfo { - /// Most recently seen IP address of the session. - pub ip: Option, - - /// Time when that the session was last active. - pub last_seen: Option, - - /// User agent string last seen in the session. - pub user_agent: Option, -} - -impl ConnectionInfo { - /// Create a new `ConnectionInfo` with all fields set to `None`. - pub fn new() -> Self { - Self::default() - } -} diff --git a/crates/ruma-client-api/src/r0/session.rs b/crates/ruma-client-api/src/r0/session.rs deleted file mode 100644 index efe178f4..00000000 --- a/crates/ruma-client-api/src/r0/session.rs +++ /dev/null @@ -1,8 +0,0 @@ -//! Endpoints for user session management. - -pub mod get_login_types; -pub mod login; -pub mod login_fallback; -pub mod logout; -pub mod logout_all; -pub mod sso_login; diff --git a/crates/ruma-client-api/src/r0/session/get_login_types.rs b/crates/ruma-client-api/src/r0/session/get_login_types.rs deleted file mode 100644 index ee06f755..00000000 --- a/crates/ruma-client-api/src/r0/session/get_login_types.rs +++ /dev/null @@ -1,396 +0,0 @@ -//! [GET /_matrix/client/r0/login](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-login) - -use std::borrow::Cow; - -use ruma_api::ruma_api; -use ruma_identifiers::MxcUri; -use ruma_serde::{JsonObject, StringEnum}; -use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value as JsonValue; - -use crate::PrivOwnedStr; - -ruma_api! { - metadata: { - description: "Gets the homeserver's supported login types to authenticate users. Clients should pick one of these and supply it as the type when logging in.", - method: GET, - name: "get_login_types", - r0_path: "/_matrix/client/r0/login", - stable_path: "/_matrix/client/v3/login", - rate_limited: true, - authentication: None, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// The homeserver's supported login types. - pub flows: Vec, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given login types. - pub fn new(flows: Vec) -> Self { - Self { flows } - } -} - -/// An authentication mechanism. -#[derive(Clone, Debug, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[serde(untagged)] -pub enum LoginType { - /// A password is supplied to authenticate. - Password(PasswordLoginType), - - /// Token-based login. - Token(TokenLoginType), - - /// SSO-based login. - Sso(SsoLoginType), - - /// Custom login type. - #[doc(hidden)] - _Custom(CustomLoginType), -} - -impl LoginType { - /// Creates a new `LoginType` with the given `login_type` string and data. - /// - /// Prefer to use the public variants of `LoginType` where possible; this constructor is meant - /// be used for unsupported login types only and does not allow setting arbitrary data for - /// supported ones. - pub fn new(login_type: &str, data: JsonObject) -> serde_json::Result { - fn from_json_object(obj: JsonObject) -> serde_json::Result { - serde_json::from_value(JsonValue::Object(obj)) - } - - Ok(match login_type { - "m.login.password" => Self::Password(from_json_object(data)?), - "m.login.token" => Self::Token(from_json_object(data)?), - "m.login.sso" => Self::Sso(from_json_object(data)?), - _ => Self::_Custom(CustomLoginType { type_: login_type.to_owned(), data }), - }) - } - - /// Returns a reference to the `login_type` string. - pub fn login_type(&self) -> &str { - match self { - Self::Password(_) => "m.login.password", - Self::Token(_) => "m.login.token", - Self::Sso(_) => "m.login.sso", - Self::_Custom(c) => &c.type_, - } - } - - /// Returns the associated data. - /// - /// Prefer to use the public variants of `LoginType` where possible; this method is meant to - /// be used for unsupported login types only. - pub fn data(&self) -> Cow<'_, JsonObject> { - fn serialize(obj: &T) -> JsonObject { - match serde_json::to_value(obj).expect("login type serialization to succeed") { - JsonValue::Object(obj) => obj, - _ => panic!("all login types must serialize to objects"), - } - } - - match self { - Self::Password(d) => Cow::Owned(serialize(d)), - Self::Token(d) => Cow::Owned(serialize(d)), - Self::Sso(d) => Cow::Owned(serialize(d)), - Self::_Custom(c) => Cow::Borrowed(&c.data), - } - } -} - -/// The payload for password login. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[serde(tag = "type", rename = "m.login.password")] -pub struct PasswordLoginType {} - -impl PasswordLoginType { - /// Creates a new `PasswordLoginType`. - pub fn new() -> Self { - Self {} - } -} - -/// The payload for token-based login. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[serde(tag = "type", rename = "m.login.token")] -pub struct TokenLoginType {} - -impl TokenLoginType { - /// Creates a new `PasswordLoginType`. - pub fn new() -> Self { - Self {} - } -} - -/// The payload for SSO login. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[serde(tag = "type", rename = "m.login.sso")] -pub struct SsoLoginType { - /// The identity provider choices. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub identity_providers: Vec, -} - -impl SsoLoginType { - /// Creates a new `PasswordLoginType`. - pub fn new() -> Self { - Self::default() - } -} - -/// An SSO login identity provider. -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct IdentityProvider { - /// The ID of the provider. - pub id: String, - - /// The display name of the provider. - pub name: String, - - /// The icon for the provider. - pub icon: Option>, - - /// The brand identifier for the provider. - pub brand: Option, -} - -impl IdentityProvider { - /// Creates an `IdentityProvider` with the given `id` and `name`. - pub fn new(id: String, name: String) -> Self { - Self { id, name, icon: None, brand: None } - } -} - -/// An SSO login identity provider brand identifier. -/// -/// The predefined ones can be found in the matrix-doc repo in a [separate document][matrix-doc]. -/// To use a custom brand string, simply use `IdentityProviderBrand::from("custom-brand")` or -/// `"custom-brand".into()` (if the type is known from the surrounding context). -/// -/// [matrix-doc]: https://github.com/matrix-org/matrix-doc/blob/v1.1/informal/idp-brands.md -#[derive(Clone, Debug, PartialEq, Eq, StringEnum)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub enum IdentityProviderBrand { - /// The [Apple] brand. - /// - /// [Apple]: https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/ - #[ruma_enum(rename = "apple")] - Apple, - - /// The [Facebook](https://developers.facebook.com/docs/facebook-login/web/login-button/) brand. - #[ruma_enum(rename = "facebook")] - Facebook, - - /// The [GitHub](https://github.com/logos) brand. - #[ruma_enum(rename = "github")] - GitHub, - - /// The [GitLab](https://about.gitlab.com/press/press-kit/) brand. - #[ruma_enum(rename = "gitlab")] - GitLab, - - /// The [Google](https://developers.google.com/identity/branding-guidelines) brand. - #[ruma_enum(rename = "google")] - Google, - - /// The [Twitter] brand. - /// - /// [Twitter]: https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter#tab1 - #[ruma_enum(rename = "twitter")] - Twitter, - - /// A custom brand. - #[doc(hidden)] - _Custom(PrivOwnedStr), -} - -/// A custom login payload. -#[doc(hidden)] -#[derive(Clone, Debug, Deserialize, Serialize)] -#[allow(clippy::exhaustive_structs)] -pub struct CustomLoginType { - /// A custom type - /// - /// This field is named `type_` instead of `type` because the latter is a reserved - /// keyword in Rust. - #[serde(rename = "type")] - pub type_: String, - - /// Remaining type content - #[serde(flatten)] - pub data: JsonObject, -} - -mod login_type_serde; - -#[cfg(test)] -mod tests { - use matches::assert_matches; - use serde::{Deserialize, Serialize}; - use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; - - use super::{ - CustomLoginType, IdentityProvider, IdentityProviderBrand, LoginType, PasswordLoginType, - SsoLoginType, TokenLoginType, - }; - - #[derive(Debug, Deserialize, Serialize)] - struct Wrapper { - pub flows: Vec, - } - - #[test] - fn deserialize_password_login_type() { - assert_matches!( - from_json_value::(json!({ - "flows": [ - { "type": "m.login.password" } - ], - })), - Ok(Wrapper { flows }) - if flows.len() == 1 - && matches!(flows[0], LoginType::Password(PasswordLoginType {})) - ); - } - - #[test] - fn deserialize_custom_login_type() { - assert_matches!( - from_json_value::(json!({ - "flows": [ - { - "type": "io.ruma.custom", - "color": "green", - } - ], - })), - Ok(Wrapper { flows }) - if flows.len() == 1 - && matches!( - &flows[0], - LoginType::_Custom(CustomLoginType { type_, data }) - if type_ == "io.ruma.custom" - && data == json!({ "color": "green" }).as_object().unwrap() - ) - ); - } - - #[test] - fn deserialize_sso_login_type() { - let mut wrapper = from_json_value::(json!({ - "flows": [ - { - "type": "m.login.sso", - "identity_providers": [ - { - "id": "oidc-gitlab", - "name": "GitLab", - "icon": "mxc://localhost/gitlab-icon", - "brand": "gitlab" - }, - { - "id": "custom", - "name": "Custom", - } - ] - } - ], - })) - .unwrap(); - - let flow = wrapper.flows.pop(); - assert_matches!(wrapper.flows.as_slice(), []); - - let mut identity_providers = match flow { - Some(LoginType::Sso(SsoLoginType { identity_providers })) => identity_providers, - _ => panic!("unexpected enum variant: {:?}", flow), - }; - - let provider = identity_providers.pop(); - assert_matches!( - provider, - Some(IdentityProvider { - id, - name, - icon: None, - brand: None, - }) if id == "custom" - && name == "Custom" - ); - - let provider = identity_providers.pop(); - assert_matches!( - provider, - Some(IdentityProvider { - id, - name, - icon: Some(icon), - brand: Some(IdentityProviderBrand::GitLab), - }) if id == "oidc-gitlab" - && name == "GitLab" - && icon == "mxc://localhost/gitlab-icon" - ); - } - - #[test] - fn serialize_sso_login_type() { - let wrapper = to_json_value(Wrapper { - flows: vec![ - LoginType::Token(TokenLoginType {}), - LoginType::Sso(SsoLoginType { - identity_providers: vec![IdentityProvider { - id: "oidc-github".into(), - name: "GitHub".into(), - icon: Some("mxc://localhost/github-icon".into()), - brand: Some(IdentityProviderBrand::GitHub), - }], - }), - ], - }) - .unwrap(); - - assert_eq!( - wrapper, - json!({ - "flows": [ - { - "type": "m.login.token" - }, - { - "type": "m.login.sso", - "identity_providers": [ - { - "id": "oidc-github", - "name": "GitHub", - "icon": "mxc://localhost/github-icon", - "brand": "github" - }, - ] - } - ], - }) - ); - } -} diff --git a/crates/ruma-client-api/src/r0/session/get_login_types/login_type_serde.rs b/crates/ruma-client-api/src/r0/session/get_login_types/login_type_serde.rs deleted file mode 100644 index a9d894c1..00000000 --- a/crates/ruma-client-api/src/r0/session/get_login_types/login_type_serde.rs +++ /dev/null @@ -1,30 +0,0 @@ -use ruma_serde::from_raw_json_value; -use serde::{de, Deserialize}; -use serde_json::value::RawValue as RawJsonValue; - -use super::LoginType; - -/// Helper struct to determine the type from a `serde_json::value::RawValue` -#[derive(Debug, Deserialize)] -struct LoginTypeDeHelper { - /// The login type field - #[serde(rename = "type")] - type_: String, -} - -impl<'de> Deserialize<'de> for LoginType { - fn deserialize(deserializer: D) -> Result - where - D: de::Deserializer<'de>, - { - let json = Box::::deserialize(deserializer)?; - let LoginTypeDeHelper { type_ } = from_raw_json_value(&json)?; - - Ok(match type_.as_ref() { - "m.login.password" => Self::Password(from_raw_json_value(&json)?), - "m.login.token" => Self::Token(from_raw_json_value(&json)?), - "m.login.sso" => Self::Sso(from_raw_json_value(&json)?), - _ => Self::_Custom(from_raw_json_value(&json)?), - }) - } -} diff --git a/crates/ruma-client-api/src/r0/session/login.rs b/crates/ruma-client-api/src/r0/session/login.rs deleted file mode 100644 index 00680938..00000000 --- a/crates/ruma-client-api/src/r0/session/login.rs +++ /dev/null @@ -1,384 +0,0 @@ -//! [POST /_matrix/client/r0/login](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-login) - -use ruma_api::ruma_api; -use ruma_identifiers::{DeviceId, ServerName, UserId}; -use ruma_serde::{JsonObject, Outgoing}; -use serde::{ - de::{self, DeserializeOwned}, - Deserialize, Deserializer, Serialize, -}; -use serde_json::Value as JsonValue; - -use crate::r0::uiaa::{IncomingUserIdentifier, UserIdentifier}; - -ruma_api! { - metadata: { - description: "Login to the homeserver.", - method: POST, - name: "login", - r0_path: "/_matrix/client/r0/login", - stable_path: "/_matrix/client/v3/login", - rate_limited: true, - authentication: None, - added: 1.0, - } - - request: { - /// The authentication mechanism. - #[serde(flatten)] - pub login_info: LoginInfo<'a>, - - /// ID of the client device - #[serde(skip_serializing_if = "Option::is_none")] - pub device_id: Option<&'a DeviceId>, - - /// A display name to assign to the newly-created device. - /// - /// Ignored if `device_id` corresponds to a known device. - #[serde(skip_serializing_if = "Option::is_none")] - pub initial_device_display_name: Option<&'a str>, - } - - response: { - /// The fully-qualified Matrix ID that has been registered. - pub user_id: Box, - - /// An access token for the account. - pub access_token: String, - - /// The hostname of the homeserver on which the account has been registered. - /// - /// Deprecated: Clients should instead use the `user_id.server_name()` - /// method if they require it. - #[serde(skip_serializing_if = "Option::is_none")] - pub home_server: Option>, - - /// ID of the logged-in device. - /// - /// Will be the same as the corresponding parameter in the request, if one was - /// specified. - pub device_id: Box, - - /// Client configuration provided by the server. - /// - /// If present, clients SHOULD use the provided object to reconfigure themselves. - #[serde(skip_serializing_if = "Option::is_none")] - pub well_known: Option, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given login info. - pub fn new(login_info: LoginInfo<'a>) -> Self { - Self { login_info, device_id: None, initial_device_display_name: None } - } -} - -impl Response { - /// Creates a new `Response` with the given user ID, access token and device ID. - pub fn new(user_id: Box, access_token: String, device_id: Box) -> Self { - Self { user_id, access_token, home_server: None, device_id, well_known: None } - } -} - -/// The authentication mechanism. -/// -/// To construct the custom `LoginInfo` variant you first have to construct -/// [`IncomingLoginInfo::new`] and then call [`IncomingLoginInfo::to_outgoing`] on it. -#[derive(Clone, Debug, Outgoing, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[incoming_derive(!Deserialize)] -#[serde(untagged)] -pub enum LoginInfo<'a> { - /// An identifier and password are supplied to authenticate. - Password(Password<'a>), - - /// Token-based login. - Token(Token<'a>), - - #[doc(hidden)] - _Custom(CustomLoginInfo<'a>), -} - -impl IncomingLoginInfo { - /// Creates a new `IncomingLoginInfo` with the given `login_type` string, session and data. - /// - /// Prefer to use the public variants of `IncomingLoginInfo` where possible; this constructor is - /// meant be used for unsupported authentication mechanisms only and does not allow setting - /// arbitrary data for supported ones. - /// - /// # Errors - /// - /// Returns an error if the `login_type` is known and serialization of `data` to the - /// corresponding `IncomingLoginInfo` variant fails. - pub fn new(login_type: &str, data: JsonObject) -> serde_json::Result { - Ok(match login_type { - "m.login.password" => Self::Password(serde_json::from_value(JsonValue::Object(data))?), - "m.login.token" => Self::Token(serde_json::from_value(JsonValue::Object(data))?), - _ => Self::_Custom(IncomingCustomLoginInfo { - login_type: login_type.into(), - extra: data, - }), - }) - } - - /// Convert `IncomingLoginInfo` to `LoginInfo`. - pub fn to_outgoing(&self) -> LoginInfo<'_> { - match self { - Self::Password(a) => LoginInfo::Password(a.to_outgoing()), - Self::Token(a) => LoginInfo::Token(a.to_outgoing()), - Self::_Custom(a) => { - LoginInfo::_Custom(CustomLoginInfo { login_type: &a.login_type, extra: &a.extra }) - } - } - } -} - -impl<'de> Deserialize<'de> for IncomingLoginInfo { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - fn from_json_value(val: JsonValue) -> Result { - serde_json::from_value(val).map_err(E::custom) - } - - // FIXME: Would be better to use serde_json::value::RawValue, but that would require - // implementing Deserialize manually for Request, bc. `#[serde(flatten)]` breaks things. - let json = JsonValue::deserialize(deserializer)?; - - let login_type = json["type"].as_str().ok_or_else(|| de::Error::missing_field("type"))?; - match login_type { - "m.login.password" => from_json_value(json).map(Self::Password), - "m.login.token" => from_json_value(json).map(Self::Token), - _ => from_json_value(json).map(Self::_Custom), - } - } -} - -/// An identifier and password to supply as authentication. -#[derive(Clone, Debug, Outgoing, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[serde(tag = "type", rename = "m.login.password")] -pub struct Password<'a> { - /// Identification information for the user. - pub identifier: UserIdentifier<'a>, - - /// The password. - pub password: &'a str, -} - -impl<'a> Password<'a> { - /// Creates a new `Password` with the given identifier and password. - pub fn new(identifier: UserIdentifier<'a>, password: &'a str) -> Self { - Self { identifier, password } - } -} - -impl IncomingPassword { - /// Convert `IncomingPassword` to `Password`. - fn to_outgoing(&self) -> Password<'_> { - Password { identifier: self.identifier.to_outgoing(), password: &self.password } - } -} - -/// A token to supply as authentication. -#[derive(Clone, Debug, Outgoing, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[serde(tag = "type", rename = "m.login.token")] -pub struct Token<'a> { - /// The token. - pub token: &'a str, -} - -impl<'a> Token<'a> { - /// Creates a new `Token` with the given token. - pub fn new(token: &'a str) -> Self { - Self { token } - } -} - -impl IncomingToken { - /// Convert `IncomingToken` to `Token`. - fn to_outgoing(&self) -> Token<'_> { - Token { token: &self.token } - } -} - -#[doc(hidden)] -#[derive(Clone, Debug, Serialize)] -#[non_exhaustive] -pub struct CustomLoginInfo<'a> { - #[serde(rename = "type")] - login_type: &'a str, - #[serde(flatten)] - extra: &'a JsonObject, -} - -#[doc(hidden)] -#[derive(Clone, Debug, Deserialize)] -#[non_exhaustive] -pub struct IncomingCustomLoginInfo { - #[serde(rename = "type")] - login_type: String, - #[serde(flatten)] - extra: JsonObject, -} - -impl Outgoing for CustomLoginInfo<'_> { - type Incoming = IncomingCustomLoginInfo; -} - -/// Client configuration provided by the server. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct DiscoveryInfo { - /// Information about the homeserver to connect to. - #[serde(rename = "m.homeserver")] - pub homeserver: HomeserverInfo, - - /// Information about the identity server to connect to. - #[serde(rename = "m.identity_server")] - pub identity_server: Option, -} - -impl DiscoveryInfo { - /// Create a new `DiscoveryInfo` with the given homeserver. - pub fn new(homeserver: HomeserverInfo) -> Self { - Self { homeserver, identity_server: None } - } -} - -/// Information about the homeserver to connect to. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct HomeserverInfo { - /// The base URL for the homeserver for client-server connections. - pub base_url: String, -} - -impl HomeserverInfo { - /// Create a new `HomeserverInfo` with the given base url. - pub fn new(base_url: String) -> Self { - Self { base_url } - } -} - -/// Information about the identity server to connect to. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct IdentityServerInfo { - /// The base URL for the identity server for client-server connections. - pub base_url: String, -} - -impl IdentityServerInfo { - /// Create a new `IdentityServerInfo` with the given base url. - pub fn new(base_url: String) -> Self { - Self { base_url } - } -} - -#[cfg(test)] -mod tests { - use matches::assert_matches; - use serde_json::{from_value as from_json_value, json}; - - use super::{IncomingLoginInfo, IncomingPassword, IncomingToken}; - use crate::r0::uiaa::IncomingUserIdentifier; - - #[test] - fn deserialize_login_type() { - assert_matches!( - from_json_value(json!({ - "type": "m.login.password", - "identifier": { - "type": "m.id.user", - "user": "cheeky_monkey" - }, - "password": "ilovebananas" - })) - .unwrap(), - IncomingLoginInfo::Password(IncomingPassword { identifier: IncomingUserIdentifier::MatrixId(user), password }) - if user == "cheeky_monkey" && password == "ilovebananas" - ); - - assert_matches!( - from_json_value(json!({ - "type": "m.login.token", - "token": "1234567890abcdef" - })) - .unwrap(), - IncomingLoginInfo::Token(IncomingToken { token }) - if token == "1234567890abcdef" - ); - } - - #[test] - #[cfg(feature = "client")] - fn serialize_login_request_body() { - use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken}; - use ruma_common::thirdparty::Medium; - use serde_json::Value as JsonValue; - - use super::{LoginInfo, Password, Request, Token}; - use crate::r0::uiaa::UserIdentifier; - - let req: http::Request> = Request { - login_info: LoginInfo::Token(Token { token: "0xdeadbeef" }), - device_id: None, - initial_device_display_name: Some("test"), - } - .try_into_http_request( - "https://homeserver.tld", - SendAccessToken::None, - &[MatrixVersion::V1_1], - ) - .unwrap(); - - let req_body_value: JsonValue = serde_json::from_slice(req.body()).unwrap(); - assert_eq!( - req_body_value, - json!({ - "type": "m.login.token", - "token": "0xdeadbeef", - "initial_device_display_name": "test", - }) - ); - - let req: http::Request> = Request { - login_info: LoginInfo::Password(Password { - identifier: UserIdentifier::ThirdPartyId { - address: "hello@example.com", - medium: Medium::Email, - }, - password: "deadbeef", - }), - device_id: None, - initial_device_display_name: Some("test"), - } - .try_into_http_request( - "https://homeserver.tld", - SendAccessToken::None, - &[MatrixVersion::V1_1], - ) - .unwrap(); - - let req_body_value: JsonValue = serde_json::from_slice(req.body()).unwrap(); - assert_eq!( - req_body_value, - json!({ - "identifier": { - "type": "m.id.thirdparty", - "medium": "email", - "address": "hello@example.com" - }, - "type": "m.login.password", - "password": "deadbeef", - "initial_device_display_name": "test", - }) - ); - } -} diff --git a/crates/ruma-client-api/src/r0/session/logout.rs b/crates/ruma-client-api/src/r0/session/logout.rs deleted file mode 100644 index 11a69e02..00000000 --- a/crates/ruma-client-api/src/r0/session/logout.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! [POST /_matrix/client/r0/logout](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-logout) - -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "Log out of the homeserver.", - method: POST, - name: "logout", - r0_path: "/_matrix/client/r0/logout", - stable_path: "/_matrix/client/v3/logout", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/session/logout_all.rs b/crates/ruma-client-api/src/r0/session/logout_all.rs deleted file mode 100644 index 492d9e8b..00000000 --- a/crates/ruma-client-api/src/r0/session/logout_all.rs +++ /dev/null @@ -1,38 +0,0 @@ -//! [POST /_matrix/client/r0/logout/all](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-logout-all) - -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "Invalidates all access tokens for a user, so that they can no longer be used for authorization.", - method: POST, - name: "logout_all", - r0_path: "/_matrix/client/r0/logout/all", - stable_path: "/_matrix/client/v3/logout/all", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/session/sso_login.rs b/crates/ruma-client-api/src/r0/session/sso_login.rs deleted file mode 100644 index 96d9df4a..00000000 --- a/crates/ruma-client-api/src/r0/session/sso_login.rs +++ /dev/null @@ -1,69 +0,0 @@ -//! [GET /_matrix/client/r0/login/sso/redirect](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-login-sso-redirect) - -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "", - method: GET, - name: "sso_login", - unstable_path: "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect", - stable_path: "/_matrix/client/v3/login/sso/redirect", - rate_limited: false, - authentication: None, - added: 1.1, - } - - request: { - /// URL to which the homeserver should return the user after completing - /// authentication with the SSO identity provider. - #[ruma_api(query)] - #[serde(rename = "redirectUrl")] - pub redirect_url: &'a str, - } - - response: { - /// Redirect URL to the SSO identity provider. - #[ruma_api(header = LOCATION)] - pub location: String, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given redirect URL. - pub fn new(redirect_url: &'a str) -> Self { - Self { redirect_url } - } -} - -impl Response { - /// Creates a new `Response` with the given SSO URL. - pub fn new(location: String) -> Self { - Self { location } - } -} - -#[cfg(all(test, feature = "client"))] -mod tests { - use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken}; - - use super::Request; - - #[test] - fn serialize_sso_login_request_uri() { - let req: http::Request> = Request { redirect_url: "https://example.com/sso" } - .try_into_http_request( - "https://homeserver.tld", - SendAccessToken::None, - &[MatrixVersion::V1_1], - ) - .unwrap(); - - assert_eq!( - req.uri().to_string(), - "https://homeserver.tld/_matrix/client/v3/login/sso/redirect?redirectUrl=https%3A%2F%2Fexample.com%2Fsso" - ); - } -} diff --git a/crates/ruma-client-api/src/r0/state/get_state_events.rs b/crates/ruma-client-api/src/r0/state/get_state_events.rs deleted file mode 100644 index 8d16f146..00000000 --- a/crates/ruma-client-api/src/r0/state/get_state_events.rs +++ /dev/null @@ -1,51 +0,0 @@ -//! [GET /_matrix/client/r0/rooms/{roomId}/state](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-state) - -use ruma_api::ruma_api; -use ruma_events::AnyStateEvent; -use ruma_identifiers::RoomId; -use ruma_serde::Raw; - -ruma_api! { - metadata: { - description: "Get state events for a room.", - method: GET, - name: "get_state_events", - r0_path: "/_matrix/client/r0/rooms/:room_id/state", - stable_path: "/_matrix/client/v3/rooms/:room_id/state", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The room to look up the state for. - #[ruma_api(path)] - pub room_id: &'a RoomId, - } - - response: { - /// If the user is a member of the room this will be the current state of the room as a - /// list of events. - /// - /// If the user has left the room then this will be the state of the room when they left as - /// a list of events. - #[ruma_api(body)] - pub room_state: Vec>, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID. - pub fn new(room_id: &'a RoomId) -> Self { - Self { room_id } - } -} - -impl Response { - /// Creates a new `Response` with the given room state. - pub fn new(room_state: Vec>) -> Self { - Self { room_state } - } -} diff --git a/crates/ruma-client-api/src/r0/state/get_state_events_for_key.rs b/crates/ruma-client-api/src/r0/state/get_state_events_for_key.rs deleted file mode 100644 index 401aafeb..00000000 --- a/crates/ruma-client-api/src/r0/state/get_state_events_for_key.rs +++ /dev/null @@ -1,159 +0,0 @@ -//! [GET /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-rooms-roomid-state-eventtype-statekey) - -use ruma_api::ruma_api; -use ruma_events::{AnyStateEventContent, EventType}; -use ruma_identifiers::RoomId; -use ruma_serde::{Outgoing, Raw}; - -ruma_api! { - metadata: { - description: "Get state events associated with a given key.", - method: GET, - name: "get_state_events_for_key", - r0_path: "/_matrix/client/r0/rooms/:room_id/state/:event_type/:state_key", - stable_path: "/_matrix/client/v3/rooms/:room_id/state/:event_type/:state_key", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - response: { - /// The content of the state event. - /// - /// Since the inner type of the `Raw` does not implement `Deserialize`, you need to use - /// `ruma_events::RawExt` to deserialize it. - #[ruma_api(body)] - pub content: Raw, - } - - error: crate::Error -} - -/// Data for a request to the `get_state_events_for_key` API endpoint. -/// -/// Get state events associated with a given key. -#[derive(Clone, Debug, Outgoing)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[incoming_derive(!Deserialize)] -pub struct Request<'a> { - /// The room to look up the state for. - pub room_id: &'a RoomId, - - /// The type of state to look up. - pub event_type: EventType, - - /// The key of the state to look up. - pub state_key: &'a str, -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room ID, event type and state key. - pub fn new(room_id: &'a RoomId, event_type: EventType, state_key: &'a str) -> Self { - Self { room_id, event_type, state_key } - } -} - -impl Response { - /// Creates a new `Response` with the given content. - pub fn new(content: Raw) -> Self { - Self { content } - } -} - -#[cfg(feature = "client")] -impl<'a> ruma_api::OutgoingRequest for Request<'a> { - type EndpointError = crate::Error; - type IncomingResponse = ::Incoming; - - const METADATA: ruma_api::Metadata = METADATA; - - fn try_into_http_request( - self, - base_url: &str, - access_token: ruma_api::SendAccessToken<'_>, - considering_versions: &'_ [ruma_api::MatrixVersion], - ) -> Result, ruma_api::error::IntoHttpError> { - use std::borrow::Cow; - - use http::header; - use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; - - let room_id_percent = utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC); - let event_type_percent = utf8_percent_encode(self.event_type.as_str(), NON_ALPHANUMERIC); - - let mut url = format!( - "{}{}", - base_url.strip_suffix('/').unwrap_or(base_url), - ruma_api::select_path( - considering_versions, - &METADATA, - None, - Some(format_args!( - "/_matrix/client/r0/rooms/{}/state/{}", - room_id_percent, event_type_percent - )), - None, - )? - ); - - if !self.state_key.is_empty() { - url.push('/'); - url.push_str(&Cow::from(utf8_percent_encode(self.state_key, NON_ALPHANUMERIC))); - } - - http::Request::builder() - .method(http::Method::GET) - .uri(url) - .header(header::CONTENT_TYPE, "application/json") - .header( - header::AUTHORIZATION, - format!( - "Bearer {}", - access_token - .get_required_for_endpoint() - .ok_or(ruma_api::error::IntoHttpError::NeedsAuthentication)?, - ), - ) - .body(T::default()) - .map_err(Into::into) - } -} - -#[cfg(feature = "server")] -impl ruma_api::IncomingRequest for IncomingRequest { - type EndpointError = crate::Error; - type OutgoingResponse = Response; - - const METADATA: ruma_api::Metadata = METADATA; - - fn try_from_http_request( - _request: http::Request, - path_args: &[S], - ) -> Result - where - B: AsRef<[u8]>, - S: AsRef, - { - // FIXME: find a way to make this if-else collapse with serde recognizing trailing Option - let (room_id, event_type, state_key): (Box, EventType, String) = - if path_args.len() == 3 { - serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::< - _, - serde::de::value::Error, - >::new( - path_args.iter().map(::std::convert::AsRef::as_ref), - ))? - } else { - let (a, b) = serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::< - _, - serde::de::value::Error, - >::new( - path_args.iter().map(::std::convert::AsRef::as_ref), - ))?; - - (a, b, "".into()) - }; - - Ok(Self { room_id, event_type, state_key }) - } -} diff --git a/crates/ruma-client-api/src/r0/state/send_state_event.rs b/crates/ruma-client-api/src/r0/state/send_state_event.rs deleted file mode 100644 index 2fadd273..00000000 --- a/crates/ruma-client-api/src/r0/state/send_state_event.rs +++ /dev/null @@ -1,187 +0,0 @@ -//! [PUT /_matrix/client/r0/rooms/{roomId}/state/{eventType}/{stateKey}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-state-eventtype-statekey) - -use ruma_api::ruma_api; -use ruma_events::{AnyStateEventContent, StateEventContent}; -use ruma_identifiers::{EventId, RoomId}; -use ruma_serde::{Outgoing, Raw}; -use serde_json::value::to_raw_value as to_raw_json_value; - -ruma_api! { - metadata: { - description: "Send a state event to a room associated with a given state key.", - method: PUT, - name: "send_state_event", - r0_path: "/_matrix/client/r0/rooms/:room_id/state/:event_type/:state_key", - stable_path: "/_matrix/client/v3/rooms/:room_id/state/:event_type/:state_key", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - response: { - /// A unique identifier for the event. - pub event_id: Box, - } - - error: crate::Error -} - -/// Data for a request to the `send_state_event` API endpoint. -/// -/// Send a state event to a room associated with a given state key. -#[derive(Clone, Debug, Outgoing)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[incoming_derive(!Deserialize)] -pub struct Request<'a> { - /// The room to set the state in. - pub room_id: &'a RoomId, - - /// The type of event to send. - pub event_type: &'a str, - - /// The state_key for the state to send. - pub state_key: &'a str, - - /// The event content to send. - pub body: Raw, -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room id, state key and event content. - /// - /// # Errors - /// - /// Since `Request` stores the request body in serialized form, this function can fail if `T`s - /// [`Serialize`][serde::Serialize] implementation can fail. - pub fn new( - room_id: &'a RoomId, - state_key: &'a str, - content: &'a T, - ) -> serde_json::Result { - Ok(Self { - room_id, - state_key, - event_type: content.event_type(), - body: Raw::from_json(to_raw_json_value(content)?), - }) - } - - /// Creates a new `Request` with the given room id, event type, state key and raw event content. - pub fn new_raw( - room_id: &'a RoomId, - event_type: &'a str, - state_key: &'a str, - body: Raw, - ) -> Self { - Self { room_id, event_type, state_key, body } - } -} - -impl Response { - /// Creates a new `Response` with the given event id. - pub fn new(event_id: Box) -> Self { - Self { event_id } - } -} - -#[cfg(feature = "client")] -impl<'a> ruma_api::OutgoingRequest for Request<'a> { - type EndpointError = crate::Error; - type IncomingResponse = Response; - - const METADATA: ruma_api::Metadata = METADATA; - - fn try_into_http_request( - self, - base_url: &str, - access_token: ruma_api::SendAccessToken<'_>, - considering_versions: &'_ [ruma_api::MatrixVersion], - ) -> Result, ruma_api::error::IntoHttpError> { - use std::borrow::Cow; - - use http::header::{self, HeaderValue}; - use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; - - let room_id_percent = utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC); - let event_type_percent = utf8_percent_encode(self.event_type, NON_ALPHANUMERIC); - - let mut url = format!( - "{}{}", - base_url.strip_suffix('/').unwrap_or(base_url), - ruma_api::select_path( - considering_versions, - &METADATA, - None, - Some(format_args!( - "/_matrix/client/r0/rooms/{}/state/{}", - room_id_percent, event_type_percent - )), - None, - )? - ); - - // Last URL segment is optional, that is why this trait impl is not generated. - if !self.state_key.is_empty() { - url.push('/'); - url.push_str(&Cow::from(utf8_percent_encode(self.state_key, NON_ALPHANUMERIC))); - } - - let http_request = http::Request::builder() - .method(http::Method::PUT) - .uri(url) - .header(header::CONTENT_TYPE, "application/json") - .header( - header::AUTHORIZATION, - HeaderValue::from_str(&format!( - "Bearer {}", - access_token - .get_required_for_endpoint() - .ok_or(ruma_api::error::IntoHttpError::NeedsAuthentication)? - ))?, - ) - .body(ruma_serde::json_to_buf(&self.body)?)?; - - Ok(http_request) - } -} - -#[cfg(feature = "server")] -impl ruma_api::IncomingRequest for IncomingRequest { - type EndpointError = crate::Error; - type OutgoingResponse = Response; - - const METADATA: ruma_api::Metadata = METADATA; - - fn try_from_http_request( - request: http::Request, - path_args: &[S], - ) -> Result - where - B: AsRef<[u8]>, - S: AsRef, - { - // FIXME: find a way to make this if-else collapse with serde recognizing trailing Option - let (room_id, event_type, state_key): (Box, String, String) = - if path_args.len() == 3 { - serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::< - _, - serde::de::value::Error, - >::new( - path_args.iter().map(::std::convert::AsRef::as_ref), - ))? - } else { - let (a, b) = serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::< - _, - serde::de::value::Error, - >::new( - path_args.iter().map(::std::convert::AsRef::as_ref), - ))?; - - (a, b, "".into()) - }; - - let body = serde_json::from_slice(request.body().as_ref())?; - - Ok(Self { room_id, event_type, state_key, body }) - } -} diff --git a/crates/ruma-client-api/src/r0/sync/sync_events.rs b/crates/ruma-client-api/src/r0/sync/sync_events.rs deleted file mode 100644 index f62fd00f..00000000 --- a/crates/ruma-client-api/src/r0/sync/sync_events.rs +++ /dev/null @@ -1,733 +0,0 @@ -//! [GET /_matrix/client/r0/sync](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-sync) - -use std::{collections::BTreeMap, time::Duration}; - -use js_int::UInt; -use ruma_api::ruma_api; -use ruma_common::presence::PresenceState; -use ruma_events::{ - presence::PresenceEvent, AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, - AnyStrippedStateEvent, AnySyncEphemeralRoomEvent, AnySyncRoomEvent, AnySyncStateEvent, - AnyToDeviceEvent, -}; -use ruma_identifiers::{DeviceKeyAlgorithm, RoomId, UserId}; -use ruma_serde::{Outgoing, Raw}; -use serde::{Deserialize, Serialize}; - -use crate::r0::filter::{FilterDefinition, IncomingFilterDefinition}; - -ruma_api! { - metadata: { - description: "Get all new events from all rooms since the last sync or a given point of time.", - method: GET, - name: "sync", - r0_path: "/_matrix/client/r0/sync", - stable_path: "/_matrix/client/v3/sync", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: { - /// A filter represented either as its full JSON definition or the ID of a saved filter. - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub filter: Option<&'a Filter<'a>>, - - /// A point in time to continue a sync from. - /// - /// Should be a token from the `next_batch` field of a previous `/sync` - /// request. - #[serde(skip_serializing_if = "Option::is_none")] - #[ruma_api(query)] - pub since: Option<&'a str>, - - /// Controls whether to include the full state for all rooms the user is a member of. - #[serde(default, skip_serializing_if = "ruma_serde::is_default")] - #[ruma_api(query)] - pub full_state: bool, - - /// Controls whether the client is automatically marked as online by polling this API. - /// - /// Defaults to `PresenceState::Online`. - #[serde(default, skip_serializing_if = "ruma_serde::is_default")] - #[ruma_api(query)] - pub set_presence: &'a PresenceState, - - /// The maximum time to poll in milliseconds before returning this request. - #[serde( - with = "ruma_serde::duration::opt_ms", - default, - skip_serializing_if = "Option::is_none", - )] - #[ruma_api(query)] - pub timeout: Option, - } - - response: { - /// The batch token to supply in the `since` param of the next `/sync` request. - pub next_batch: String, - - /// Updates to rooms. - #[serde(default, skip_serializing_if = "Rooms::is_empty")] - pub rooms: Rooms, - - /// Updates to the presence status of other users. - #[serde(default, skip_serializing_if = "Presence::is_empty")] - pub presence: Presence, - - /// The global private data created by this user. - #[serde(default, skip_serializing_if = "GlobalAccountData::is_empty")] - pub account_data: GlobalAccountData, - - /// Messages sent directly between devices. - #[serde(default, skip_serializing_if = "ToDevice::is_empty")] - pub to_device: ToDevice, - - /// Information on E2E device updates. - /// - /// Only present on an incremental sync. - #[serde(default, skip_serializing_if = "DeviceLists::is_empty")] - pub device_lists: DeviceLists, - - /// For each key algorithm, the number of unclaimed one-time keys - /// currently held on the server for a device. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub device_one_time_keys_count: BTreeMap, - - /// For each key algorithm, the number of unclaimed one-time keys - /// currently held on the server for a device. - /// - /// The presence of this field indicates that the server supports - /// fallback keys. - pub device_unused_fallback_key_types: Option>, - } - - error: crate::Error -} - -impl Request<'_> { - /// Creates an empty `Request`. - pub fn new() -> Self { - Default::default() - } -} - -impl Response { - /// Creates a new `Response` with the given batch token. - pub fn new(next_batch: String) -> Self { - Self { - next_batch, - rooms: Default::default(), - presence: Default::default(), - account_data: Default::default(), - to_device: Default::default(), - device_lists: Default::default(), - device_one_time_keys_count: BTreeMap::new(), - device_unused_fallback_key_types: None, - } - } -} - -/// A filter represented either as its full JSON definition or the ID of a saved filter. -#[derive(Clone, Debug, Outgoing, Serialize)] -#[allow(clippy::large_enum_variant)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -#[serde(untagged)] -pub enum Filter<'a> { - // The filter definition needs to be (de)serialized twice because it is a URL-encoded JSON - // string. Since #[ruma_api(query)] only does the latter and this is a very uncommon - // setup, we implement it through custom serde logic for this specific enum variant rather - // than adding another ruma_api attribute. - // - // On the deserialization side, because this is an enum with #[serde(untagged)], serde will - // try the variants in order (https://serde.rs/enum-representations.html). That means because - // FilterDefinition is the first variant, JSON decoding is attempted first which is almost - // functionally equivalent to looking at whether the first symbol is a '{' as the spec says. - // (there are probably some corner cases like leading whitespace) - /// A complete filter definition serialized to JSON. - #[serde(with = "ruma_serde::json_string")] - FilterDefinition(FilterDefinition<'a>), - - /// The ID of a filter saved on the server. - FilterId(&'a str), -} - -impl<'a> From> for Filter<'a> { - fn from(def: FilterDefinition<'a>) -> Self { - Self::FilterDefinition(def) - } -} - -impl<'a> From<&'a str> for Filter<'a> { - fn from(id: &'a str) -> Self { - Self::FilterId(id) - } -} - -/// Updates to rooms. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Rooms { - /// The rooms that the user has left or been banned from. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub leave: BTreeMap, LeftRoom>, - - /// The rooms that the user has joined. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub join: BTreeMap, JoinedRoom>, - - /// The rooms that the user has been invited to. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub invite: BTreeMap, InvitedRoom>, - - /// The rooms that the user has knocked on. - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub knock: BTreeMap, KnockedRoom>, -} - -impl Rooms { - /// Creates an empty `Rooms`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there is no update in any room. - pub fn is_empty(&self) -> bool { - self.leave.is_empty() && self.join.is_empty() && self.invite.is_empty() - } -} - -/// Historical updates to left rooms. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct LeftRoom { - /// The timeline of messages and state changes in the room up to the point when the user - /// left. - #[serde(default, skip_serializing_if = "Timeline::is_empty")] - pub timeline: Timeline, - - /// The state updates for the room up to the start of the timeline. - #[serde(default, skip_serializing_if = "State::is_empty")] - pub state: State, - - /// The private data that this user has attached to this room. - #[serde(default, skip_serializing_if = "RoomAccountData::is_empty")] - pub account_data: RoomAccountData, -} - -impl LeftRoom { - /// Creates an empty `LeftRoom`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are updates in the room. - pub fn is_empty(&self) -> bool { - self.timeline.is_empty() && self.state.is_empty() && self.account_data.is_empty() - } -} - -/// Updates to joined rooms. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct JoinedRoom { - /// Information about the room which clients may need to correctly render it - /// to users. - #[serde(default, skip_serializing_if = "RoomSummary::is_empty")] - pub summary: RoomSummary, - - /// Counts of unread notifications for this room. - #[serde(default, skip_serializing_if = "UnreadNotificationsCount::is_empty")] - pub unread_notifications: UnreadNotificationsCount, - - /// The timeline of messages and state changes in the room. - #[serde(default, skip_serializing_if = "Timeline::is_empty")] - pub timeline: Timeline, - - /// Updates to the state, between the time indicated by the `since` parameter, and the start - /// of the `timeline` (or all state up to the start of the `timeline`, if `since` is not - /// given, or `full_state` is true). - #[serde(default, skip_serializing_if = "State::is_empty")] - pub state: State, - - /// The private data that this user has attached to this room. - #[serde(default, skip_serializing_if = "RoomAccountData::is_empty")] - pub account_data: RoomAccountData, - - /// The ephemeral events in the room that aren't recorded in the timeline or state of the room. - #[serde(default, skip_serializing_if = "Ephemeral::is_empty")] - pub ephemeral: Ephemeral, -} - -impl JoinedRoom { - /// Creates an empty `JoinedRoom`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no updates in the room. - pub fn is_empty(&self) -> bool { - self.summary.is_empty() - && self.unread_notifications.is_empty() - && self.timeline.is_empty() - && self.state.is_empty() - && self.account_data.is_empty() - && self.ephemeral.is_empty() - } -} - -/// Updates to knocked rooms. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct KnockedRoom { - /// The knock state. - pub knock_state: KnockState, -} - -/// A mapping from a key `events` to a list of `StrippedStateEvent`. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct KnockState { - /// The list of events. - pub events: Vec>, -} - -/// Unread notifications count. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct UnreadNotificationsCount { - /// The number of unread notifications for this room with the highlight flag set. - #[serde(skip_serializing_if = "Option::is_none")] - pub highlight_count: Option, - - /// The total number of unread notifications for this room. - #[serde(skip_serializing_if = "Option::is_none")] - pub notification_count: Option, -} - -impl UnreadNotificationsCount { - /// Creates an empty `UnreadNotificationsCount`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no notification count updates. - pub fn is_empty(&self) -> bool { - self.highlight_count.is_none() && self.notification_count.is_none() - } -} - -/// Events in the room. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Timeline { - /// True if the number of events returned was limited by the `limit` on the filter. - /// - /// Default to `false`. - #[serde(default, skip_serializing_if = "ruma_serde::is_default")] - pub limited: bool, - - /// A token that can be supplied to to the `from` parameter of the `/rooms/{roomId}/messages` - /// endpoint. - #[serde(skip_serializing_if = "Option::is_none")] - pub prev_batch: Option, - - /// A list of events. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec>, -} - -impl Timeline { - /// Creates an empty `Timeline`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no timeline updates. - pub fn is_empty(&self) -> bool { - self.events.is_empty() - } -} - -/// State events in the room. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct State { - /// A list of state events. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec>, -} - -impl State { - /// Creates an empty `State`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no state updates. - pub fn is_empty(&self) -> bool { - self.events.is_empty() - } -} - -/// The global private data created by this user. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct GlobalAccountData { - /// A list of events. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec>, -} - -impl GlobalAccountData { - /// Creates an empty `GlobalAccountData`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no global account data updates. - pub fn is_empty(&self) -> bool { - self.events.is_empty() - } -} - -/// The private data that this user has attached to this room. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct RoomAccountData { - /// A list of events. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec>, -} - -impl RoomAccountData { - /// Creates an empty `RoomAccountData`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no room account data updates. - pub fn is_empty(&self) -> bool { - self.events.is_empty() - } -} - -/// Ephemeral events not recorded in the timeline or state of the room. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Ephemeral { - /// A list of events. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec>, -} - -impl Ephemeral { - /// Creates an empty `Ephemeral`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no ephemeral event updates. - pub fn is_empty(&self) -> bool { - self.events.is_empty() - } -} - -/// Information about room for rendering to clients. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct RoomSummary { - /// Users which can be used to generate a room name if the room does not have one. - /// - /// Required if room name or canonical aliases are not set or empty. - #[serde(rename = "m.heroes", default, skip_serializing_if = "Vec::is_empty")] - pub heroes: Vec, - - /// Number of users whose membership status is `join`. - /// Required if field has changed since last sync; otherwise, it may be - /// omitted. - #[serde(rename = "m.joined_member_count", skip_serializing_if = "Option::is_none")] - pub joined_member_count: Option, - - /// Number of users whose membership status is `invite`. - /// Required if field has changed since last sync; otherwise, it may be - /// omitted. - #[serde(rename = "m.invited_member_count", skip_serializing_if = "Option::is_none")] - pub invited_member_count: Option, -} - -impl RoomSummary { - /// Creates an empty `RoomSummary`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no room summary updates. - pub fn is_empty(&self) -> bool { - self.heroes.is_empty() - && self.joined_member_count.is_none() - && self.invited_member_count.is_none() - } -} - -/// Updates to the rooms that the user has been invited to. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct InvitedRoom { - /// The state of a room that the user has been invited to. - #[serde(default, skip_serializing_if = "InviteState::is_empty")] - pub invite_state: InviteState, -} - -impl InvitedRoom { - /// Creates an empty `InvitedRoom`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no updates to this room. - pub fn is_empty(&self) -> bool { - self.invite_state.is_empty() - } -} - -/// The state of a room that the user has been invited to. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct InviteState { - /// A list of state events. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec>, -} - -impl InviteState { - /// Creates an empty `InviteState`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no state updates. - pub fn is_empty(&self) -> bool { - self.events.is_empty() - } -} - -/// Updates to the presence status of other users. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct Presence { - /// A list of events. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec>, -} - -impl Presence { - /// Creates an empty `Presence`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no presence updates. - pub fn is_empty(&self) -> bool { - self.events.is_empty() - } -} - -/// Messages sent directly between devices. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct ToDevice { - /// A list of to-device events. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub events: Vec>, -} - -impl ToDevice { - /// Creates an empty `ToDevice`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no to-device events. - pub fn is_empty(&self) -> bool { - self.events.is_empty() - } -} - -/// Information on E2E device updates. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct DeviceLists { - /// List of users who have updated their device identity keys or who now - /// share an encrypted room with the client since the previous sync - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub changed: Vec>, - - /// List of users who no longer share encrypted rooms since the previous sync - /// response. - #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub left: Vec>, -} - -impl DeviceLists { - /// Creates an empty `DeviceLists`. - pub fn new() -> Self { - Default::default() - } - - /// Returns true if there are no device list updates. - pub fn is_empty(&self) -> bool { - self.changed.is_empty() && self.left.is_empty() - } -} - -#[cfg(test)] -mod tests { - use assign::assign; - use matches::assert_matches; - use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; - - use super::Timeline; - - #[test] - fn timeline_serde() { - let timeline = assign!(Timeline::new(), { limited: true }); - let timeline_serialized = json!({ "limited": true }); - assert_eq!(to_json_value(timeline).unwrap(), timeline_serialized); - - let timeline_deserialized = from_json_value(timeline_serialized); - assert_matches!(timeline_deserialized, Ok(Timeline { limited: true, .. })); - - let timeline_default = Timeline::default(); - assert_eq!(to_json_value(timeline_default).unwrap(), json!({})); - - let timeline_default_deserialized = from_json_value(json!({})); - assert_matches!(timeline_default_deserialized, Ok(Timeline { limited: false, .. })); - } -} - -#[cfg(all(test, feature = "client"))] -mod client_tests { - use std::time::Duration; - - use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken}; - - use super::{Filter, PresenceState, Request}; - - #[test] - fn serialize_all_params() { - let req: http::Request> = Request { - filter: Some(&Filter::FilterId("66696p746572")), - since: Some("s72594_4483_1934"), - full_state: true, - set_presence: &PresenceState::Offline, - timeout: Some(Duration::from_millis(30000)), - } - .try_into_http_request( - "https://homeserver.tld", - SendAccessToken::IfRequired("auth_tok"), - &[MatrixVersion::V1_1], - ) - .unwrap(); - - let uri = req.uri(); - let query = uri.query().unwrap(); - - assert_eq!(uri.path(), "/_matrix/client/v3/sync"); - assert!(query.contains("filter=66696p746572")); - assert!(query.contains("since=s72594_4483_1934")); - assert!(query.contains("full_state=true")); - assert!(query.contains("set_presence=offline")); - assert!(query.contains("timeout=30000")) - } -} - -#[cfg(all(test, feature = "server"))] -mod server_tests { - use std::time::Duration; - - use matches::assert_matches; - use ruma_api::IncomingRequest as _; - use ruma_common::presence::PresenceState; - - use super::{IncomingFilter, IncomingRequest}; - - #[test] - fn deserialize_all_query_params() { - let uri = http::Uri::builder() - .scheme("https") - .authority("matrix.org") - .path_and_query( - "/_matrix/client/r0/sync\ - ?filter=myfilter\ - &since=myts\ - &full_state=false\ - &set_presence=offline\ - &timeout=5000", - ) - .build() - .unwrap(); - - let req = IncomingRequest::try_from_http_request( - http::Request::builder().uri(uri).body(&[] as &[u8]).unwrap(), - &[] as &[String], - ) - .unwrap(); - - assert_matches!(req.filter, Some(IncomingFilter::FilterId(id)) if id == "myfilter"); - assert_eq!(req.since, Some("myts".into())); - assert!(!req.full_state); - assert_eq!(req.set_presence, PresenceState::Offline); - assert_eq!(req.timeout, Some(Duration::from_millis(5000))); - } - - #[test] - fn deserialize_no_query_params() { - let uri = http::Uri::builder() - .scheme("https") - .authority("matrix.org") - .path_and_query("/_matrix/client/r0/sync") - .build() - .unwrap(); - - let req = IncomingRequest::try_from_http_request( - http::Request::builder().uri(uri).body(&[] as &[u8]).unwrap(), - &[] as &[String], - ) - .unwrap(); - - assert_matches!(req.filter, None); - assert_eq!(req.since, None); - assert!(!req.full_state); - assert_eq!(req.set_presence, PresenceState::Online); - assert_eq!(req.timeout, None); - } - - #[test] - fn deserialize_some_query_params() { - let uri = http::Uri::builder() - .scheme("https") - .authority("matrix.org") - .path_and_query( - "/_matrix/client/r0/sync\ - ?filter=EOKFFmdZYF\ - &timeout=0", - ) - .build() - .unwrap(); - - let req = IncomingRequest::try_from_http_request( - http::Request::builder().uri(uri).body(&[] as &[u8]).unwrap(), - &[] as &[String], - ) - .unwrap(); - - assert_matches!(req.filter, Some(IncomingFilter::FilterId(id)) if id == "EOKFFmdZYF"); - assert_eq!(req.since, None); - assert!(!req.full_state); - assert_eq!(req.set_presence, PresenceState::Online); - assert_eq!(req.timeout, Some(Duration::from_millis(0))); - } -} diff --git a/crates/ruma-client-api/src/r0/tag/create_tag.rs b/crates/ruma-client-api/src/r0/tag/create_tag.rs deleted file mode 100644 index ebfaa591..00000000 --- a/crates/ruma-client-api/src/r0/tag/create_tag.rs +++ /dev/null @@ -1,55 +0,0 @@ -//! [PUT /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-user-userid-rooms-roomid-tags-tag) - -use ruma_api::ruma_api; -use ruma_events::tag::TagInfo; -use ruma_identifiers::{RoomId, UserId}; - -ruma_api! { - metadata: { - description: "Add a new tag to a room.", - method: PUT, - name: "create_tag", - r0_path: "/_matrix/client/r0/user/:user_id/rooms/:room_id/tags/:tag", - stable_path: "/_matrix/client/v3/user/:user_id/rooms/:room_id/tags/:tag", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The ID of the user creating the tag. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// The room to tag. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The name of the tag to create. - #[ruma_api(path)] - pub tag: &'a str, - - /// Info about the tag. - #[ruma_api(body)] - pub tag_info: TagInfo, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID, room ID, tag and tag info. - pub fn new(user_id: &'a UserId, room_id: &'a RoomId, tag: &'a str, tag_info: TagInfo) -> Self { - Self { user_id, room_id, tag, tag_info } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/tag/delete_tag.rs b/crates/ruma-client-api/src/r0/tag/delete_tag.rs deleted file mode 100644 index 9a341542..00000000 --- a/crates/ruma-client-api/src/r0/tag/delete_tag.rs +++ /dev/null @@ -1,50 +0,0 @@ -//! [DELETE /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags/{tag}](https://matrix.org/docs/spec/client_server/r0.6.1#delete-matrix-client-r0-user-userid-rooms-roomid-tags-tag) - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomId, UserId}; - -ruma_api! { - metadata: { - description: "Remove a tag from a room.", - method: DELETE, - name: "delete_tag", - r0_path: "/_matrix/client/r0/user/:user_id/rooms/:room_id/tags/:tag", - stable_path: "/_matrix/client/v3/user/:user_id/rooms/:room_id/tags/:tag", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The user whose tag will be deleted. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// The tagged room. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// The name of the tag to delete. - #[ruma_api(path)] - pub tag: &'a str, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID, room ID and tag - pub fn new(user_id: &'a UserId, room_id: &'a RoomId, tag: &'a str) -> Self { - Self { user_id, room_id, tag } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} diff --git a/crates/ruma-client-api/src/r0/tag/get_tags.rs b/crates/ruma-client-api/src/r0/tag/get_tags.rs deleted file mode 100644 index 12992f33..00000000 --- a/crates/ruma-client-api/src/r0/tag/get_tags.rs +++ /dev/null @@ -1,85 +0,0 @@ -//! [GET /_matrix/client/r0/user/{userId}/rooms/{roomId}/tags](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-user-userid-rooms-roomid-tags) - -use ruma_api::ruma_api; -use ruma_events::tag::Tags; -use ruma_identifiers::{RoomId, UserId}; - -ruma_api! { - metadata: { - description: "Get the tags associated with a room.", - method: GET, - name: "get_tags", - r0_path: "/_matrix/client/r0/user/:user_id/rooms/:room_id/tags", - stable_path: "/_matrix/client/v3/user/:user_id/rooms/:room_id/tags", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The user whose tags will be retrieved. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// The room from which tags will be retrieved. - #[ruma_api(path)] - pub room_id: &'a RoomId, - } - - response: { - /// The user's tags for the room. - pub tags: Tags, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID and room ID. - pub fn new(user_id: &'a UserId, room_id: &'a RoomId) -> Self { - Self { user_id, room_id } - } -} - -impl Response { - /// Creates a new `Response` with the given tags. - pub fn new(tags: Tags) -> Self { - Self { tags } - } -} - -#[cfg(all(test, feature = "server"))] -mod server_tests { - use assign::assign; - use ruma_api::OutgoingResponse; - use ruma_events::tag::{TagInfo, Tags}; - use serde_json::json; - - use super::Response; - - #[test] - fn serializing_get_tags_response() { - let mut tags = Tags::new(); - tags.insert("m.favourite".into(), assign!(TagInfo::new(), { order: Some(0.25) })); - tags.insert("u.user_tag".into(), assign!(TagInfo::new(), { order: Some(0.11) })); - let response = Response { tags }; - - let http_response = response.try_into_http_response::>().unwrap(); - - let json_response: serde_json::Value = - serde_json::from_slice(http_response.body()).unwrap(); - assert_eq!( - json_response, - json!({ - "tags": { - "m.favourite": { - "order": 0.25, - }, - "u.user_tag": { - "order": 0.11, - } - } - }) - ); - } -} diff --git a/crates/ruma-client-api/src/r0/thirdparty/get_location_for_protocol.rs b/crates/ruma-client-api/src/r0/thirdparty/get_location_for_protocol.rs deleted file mode 100644 index 15da6457..00000000 --- a/crates/ruma-client-api/src/r0/thirdparty/get_location_for_protocol.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! [GET /_matrix/client/r0/thirdparty/location/{protocol}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-thirdparty-location-protocol) - -use std::collections::BTreeMap; - -use ruma_api::ruma_api; -use ruma_common::thirdparty::Location; - -ruma_api! { - metadata: { - description: "Fetches third party locations for a protocol.", - method: GET, - name: "get_location_for_protocol", - r0_path: "/_matrix/client/r0/thirdparty/location/:protocol", - stable_path: "/_matrix/client/v3/thirdparty/location/:protocol", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The protocol used to communicate to the third party network. - #[ruma_api(path)] - pub protocol: &'a str, - - /// One or more custom fields to help identify the third party location. - // The specification is incorrect for this parameter. See matrix-org/matrix-doc#2352. - #[ruma_api(query_map)] - pub fields: BTreeMap, - } - - response: { - /// List of matched third party locations. - #[ruma_api(body)] - pub locations: Vec, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given protocol. - pub fn new(protocol: &'a str) -> Self { - Self { protocol, fields: BTreeMap::new() } - } -} - -impl Response { - /// Creates a new `Response` with the given locations. - pub fn new(locations: Vec) -> Self { - Self { locations } - } -} diff --git a/crates/ruma-client-api/src/r0/thirdparty/get_location_for_room_alias.rs b/crates/ruma-client-api/src/r0/thirdparty/get_location_for_room_alias.rs deleted file mode 100644 index 614c411b..00000000 --- a/crates/ruma-client-api/src/r0/thirdparty/get_location_for_room_alias.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! [GET /_matrix/client/r0/thirdparty/location](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-thirdparty-location) - -use ruma_api::ruma_api; -use ruma_common::thirdparty::Location; -use ruma_identifiers::RoomAliasId; - -ruma_api! { - metadata: { - description: "Retrieve an array of third party network locations from a Matrix room alias.", - method: GET, - name: "get_location_for_room_alias", - r0_path: "/_matrix/client/r0/thirdparty/location", - stable_path: "/_matrix/client/v3/thirdparty/location", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The Matrix room alias to look up. - #[ruma_api(query)] - pub alias: &'a RoomAliasId, - } - - response: { - /// List of matched third party locations. - #[ruma_api(body)] - pub locations: Vec, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given room alias ID. - pub fn new(alias: &'a RoomAliasId) -> Self { - Self { alias } - } -} - -impl Response { - /// Creates a new `Response` with the given locations. - pub fn new(locations: Vec) -> Self { - Self { locations } - } -} diff --git a/crates/ruma-client-api/src/r0/thirdparty/get_protocol.rs b/crates/ruma-client-api/src/r0/thirdparty/get_protocol.rs deleted file mode 100644 index d07228bf..00000000 --- a/crates/ruma-client-api/src/r0/thirdparty/get_protocol.rs +++ /dev/null @@ -1,45 +0,0 @@ -//! [GET /_matrix/client/r0/thirdparty/protocol/{protocol}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-thirdparty-protocol-protocol) - -use ruma_api::ruma_api; -use ruma_common::thirdparty::Protocol; - -ruma_api! { - metadata: { - description: "Fetches the metadata from the homeserver about a particular third party protocol.", - method: GET, - name: "get_protocol", - r0_path: "/_matrix/client/r0/thirdparty/protocol/:protocol", - stable_path: "/_matrix/client/v3/thirdparty/protocol/:protocol", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The name of the protocol. - #[ruma_api(path)] - pub protocol: &'a str, - } - - response: { - /// Metadata about the protocol. - #[ruma_api(body)] - pub protocol: Protocol, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given protocol name. - pub fn new(protocol: &'a str) -> Self { - Self { protocol } - } -} - -impl Response { - /// Creates a new `Response` with the given protocol. - pub fn new(protocol: Protocol) -> Self { - Self { protocol } - } -} diff --git a/crates/ruma-client-api/src/r0/thirdparty/get_protocols.rs b/crates/ruma-client-api/src/r0/thirdparty/get_protocols.rs deleted file mode 100644 index f925d547..00000000 --- a/crates/ruma-client-api/src/r0/thirdparty/get_protocols.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! [GET /_matrix/client/r0/thirdparty/protocols](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-thirdparty-protocols) - -use std::collections::BTreeMap; - -use ruma_api::ruma_api; -use ruma_common::thirdparty::Protocol; - -ruma_api! { - metadata: { - description: "Fetches the overall metadata about protocols supported by the homeserver.", - method: GET, - name: "get_protocols", - r0_path: "/_matrix/client/r0/thirdparty/protocols", - stable_path: "/_matrix/client/v3/thirdparty/protocols", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// Metadata about protocols supported by the homeserver. - #[ruma_api(body)] - pub protocols: BTreeMap, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given protocols. - pub fn new(protocols: BTreeMap) -> Self { - Self { protocols } - } -} diff --git a/crates/ruma-client-api/src/r0/thirdparty/get_user_for_protocol.rs b/crates/ruma-client-api/src/r0/thirdparty/get_user_for_protocol.rs deleted file mode 100644 index 0c1e2d70..00000000 --- a/crates/ruma-client-api/src/r0/thirdparty/get_user_for_protocol.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! [GET /_matrix/client/r0/thirdparty/user/{protocol}](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-thirdparty-user-protocol) - -use std::collections::BTreeMap; - -use ruma_api::ruma_api; -use ruma_common::thirdparty::User; - -ruma_api! { - metadata: { - description: "Fetches third party users for a protocol.", - method: GET, - name: "get_user_for_protocol", - r0_path: "/_matrix/client/r0/thirdparty/user/:protocol", - stable_path: "/_matrix/client/v3/thirdparty/user/:protocol", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The protocol used to communicate to the third party network. - #[ruma_api(path)] - pub protocol: &'a str, - - /// One or more custom fields that are passed to the AS to help identify the user. - // The specification is incorrect for this parameter. See matrix-org/matrix-doc#2352. - #[ruma_api(query_map)] - pub fields: BTreeMap, - } - - response: { - /// List of matched third party users. - #[ruma_api(body)] - pub users: Vec, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given protocol. - pub fn new(protocol: &'a str) -> Self { - Self { protocol, fields: BTreeMap::new() } - } -} - -impl Response { - /// Creates a new `Response` with the given users. - pub fn new(users: Vec) -> Self { - Self { users } - } -} diff --git a/crates/ruma-client-api/src/r0/thirdparty/get_user_for_user_id.rs b/crates/ruma-client-api/src/r0/thirdparty/get_user_for_user_id.rs deleted file mode 100644 index 7e150af5..00000000 --- a/crates/ruma-client-api/src/r0/thirdparty/get_user_for_user_id.rs +++ /dev/null @@ -1,46 +0,0 @@ -//! [GET /_matrix/client/r0/thirdparty/user](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-thirdparty-user) - -use ruma_api::ruma_api; -use ruma_common::thirdparty::User; -use ruma_identifiers::UserId; - -ruma_api! { - metadata: { - description: "Retrieve an array of third party users from a Matrix User ID.", - method: GET, - name: "get_user_for_user_id", - r0_path: "/_matrix/client/r0/thirdparty/user", - stable_path: "/_matrix/client/v3/thirdparty/user", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The Matrix User ID to look up. - #[ruma_api(query)] - pub userid: &'a UserId, - } - - response: { - /// List of matched third party users. - #[ruma_api(body)] - pub users: Vec, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID. - pub fn new(userid: &'a UserId) -> Self { - Self { userid } - } -} - -impl Response { - /// Creates a new `Response` with the given users. - pub fn new(users: Vec) -> Self { - Self { users } - } -} diff --git a/crates/ruma-client-api/src/r0/to_device/send_event_to_device.rs b/crates/ruma-client-api/src/r0/to_device/send_event_to_device.rs deleted file mode 100644 index 5ede8de0..00000000 --- a/crates/ruma-client-api/src/r0/to_device/send_event_to_device.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! [PUT /_matrix/client/r0/sendToDevice/{eventType}/{txnId}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-sendtodevice-eventtype-txnid) - -use std::collections::BTreeMap; - -use ruma_api::ruma_api; -use ruma_common::to_device::DeviceIdOrAllDevices; -use ruma_events::AnyToDeviceEventContent; -use ruma_identifiers::{TransactionId, UserId}; -use ruma_serde::Raw; - -ruma_api! { - metadata: { - description: "Send an event to a device or devices.", - method: PUT, - name: "send_event_to_device", - r0_path: "/_matrix/client/r0/sendToDevice/:event_type/:txn_id", - stable_path: "/_matrix/client/v3/sendToDevice/:event_type/:txn_id", - rate_limited: false, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// Type of event being sent to each device. - #[ruma_api(path)] - pub event_type: &'a str, - - /// A request identifier unique to the access token used to send the request. - #[ruma_api(path)] - pub txn_id: &'a TransactionId, - - /// Messages to send. - /// - /// Different message events can be sent to different devices in the same request, but all - /// events within one request must be of the same type. - pub messages: Messages, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given event type, transaction ID and raw messages. - pub fn new_raw(event_type: &'a str, txn_id: &'a TransactionId, messages: Messages) -> Self { - Self { event_type, txn_id, messages } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} - -/// Messages to send in a send-to-device request. -/// -/// Represented as a map of `{ user-ids => { device-ids => message-content } }`. -pub type Messages = - BTreeMap, BTreeMap>>; diff --git a/crates/ruma-client-api/src/r0/typing/create_typing_event.rs b/crates/ruma-client-api/src/r0/typing/create_typing_event.rs deleted file mode 100644 index 70d58c4e..00000000 --- a/crates/ruma-client-api/src/r0/typing/create_typing_event.rs +++ /dev/null @@ -1,101 +0,0 @@ -//! [PUT /_matrix/client/r0/rooms/{roomId}/typing/{userId}](https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-rooms-roomid-typing-userid) - -use std::time::Duration; - -use ruma_api::ruma_api; -use ruma_identifiers::{RoomId, UserId}; -use serde::{de::Error, Deserialize, Deserializer, Serialize}; - -ruma_api! { - metadata: { - method: PUT, - r0_path: "/_matrix/client/r0/rooms/:room_id/typing/:user_id", - stable_path: "/_matrix/client/v3/rooms/:room_id/typing/:user_id", - name: "create_typing_event", - description: "Send a typing event to a room.", - authentication: AccessToken, - rate_limited: true, - added: 1.0, - } - - request: { - /// The user who has started to type. - #[ruma_api(path)] - pub user_id: &'a UserId, - - /// The room in which the user is typing. - #[ruma_api(path)] - pub room_id: &'a RoomId, - - /// Whether the user is typing within a length of time or not. - #[serde(flatten)] - pub state: Typing, - } - - #[derive(Default)] - response: {} - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given user ID, room ID and typing state. - pub fn new(user_id: &'a UserId, room_id: &'a RoomId, state: Typing) -> Self { - Self { user_id, room_id, state } - } -} - -impl Response { - /// Creates an empty `Response`. - pub fn new() -> Self { - Self {} - } -} - -/// A mark for whether the user is typing within a length of time or not. -#[derive(Clone, Copy, Debug, Serialize)] -#[serde(into = "TypingInner")] -#[allow(clippy::exhaustive_enums)] -pub enum Typing { - /// Not typing. - No, - - /// Typing during the specified length of time. - Yes(Duration), -} - -#[derive(Deserialize, Serialize)] -struct TypingInner { - typing: bool, - - #[serde( - with = "ruma_serde::duration::opt_ms", - default, - skip_serializing_if = "Option::is_none" - )] - timeout: Option, -} - -impl From for TypingInner { - fn from(typing: Typing) -> Self { - match typing { - Typing::No => Self { typing: false, timeout: None }, - Typing::Yes(time) => Self { typing: true, timeout: Some(time) }, - } - } -} - -impl<'de> Deserialize<'de> for Typing { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let inner = TypingInner::deserialize(deserializer)?; - - match (inner.typing, inner.timeout) { - (false, _) => Ok(Self::No), - (true, Some(time)) => Ok(Self::Yes(time)), - _ => Err(D::Error::missing_field("timeout")), - } - } -} diff --git a/crates/ruma-client-api/src/r0/uiaa/get_uiaa_fallback_page.rs b/crates/ruma-client-api/src/r0/uiaa/get_uiaa_fallback_page.rs deleted file mode 100644 index 5a760552..00000000 --- a/crates/ruma-client-api/src/r0/uiaa/get_uiaa_fallback_page.rs +++ /dev/null @@ -1,58 +0,0 @@ -//! [GET /_matrix/client/r0/auth/{auth_type}/fallback/web?session={session_id}](https://matrix.org/docs/spec/client_server/r0.6.1#fallback) - -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "Get UIAA fallback web page.", - method: GET, - name: "authorize_fallback", - r0_path: "/_matrix/client/r0/auth/:auth_type/fallback/web", - stable_path: "/_matrix/client/v3/auth/:auth_type/fallback/web", - rate_limited: false, - authentication: None, - added: 1.0, - } - - request: { - /// The type name ("m.login.dummy", etc.) of the uiaa stage to get a fallback page for. - #[ruma_api(path)] - pub auth_type: String, - - /// The ID of the session given by the homeserver. - #[ruma_api(query)] - pub session: String, - } - - #[derive(Default)] - response: { - /// Optional URI to redirect to. - #[ruma_api(header = LOCATION)] - pub redirect_url: Option, - - /// HTML to return to client. - #[ruma_api(raw_body)] - pub body: Vec, - } - - error: crate::Error -} - -impl Request { - /// Creates a new `Request` with the given auth type and session ID. - pub fn new(auth_type: String, session: String) -> Self { - Self { auth_type, session } - } -} - -impl Response { - /// Creates a new `Response` with the given HTML body. - pub fn new(body: Vec) -> Self { - Self { redirect_url: None, body } - } - - /// Creates a new `Response` with the given redirect URL and an empty body. - pub fn redirect(url: String) -> Self { - Self { redirect_url: Some(url), body: Vec::new() } - } -} diff --git a/crates/ruma-client-api/src/r0/user_directory/search_users.rs b/crates/ruma-client-api/src/r0/user_directory/search_users.rs deleted file mode 100644 index f7bb7bad..00000000 --- a/crates/ruma-client-api/src/r0/user_directory/search_users.rs +++ /dev/null @@ -1,100 +0,0 @@ -//! [POST /_matrix/client/r0/user_directory/search](https://matrix.org/docs/spec/client_server/r0.6.1#post-matrix-client-r0-user-directory-search) - -use js_int::{uint, UInt}; -use ruma_api::ruma_api; -use ruma_identifiers::{MxcUri, UserId}; -use serde::{Deserialize, Serialize}; - -ruma_api! { - metadata: { - description: "Performs a search for users.", - method: POST, - name: "search_users", - r0_path: "/_matrix/client/r0/user_directory/search", - stable_path: "/_matrix/client/v3/user_directory/search", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - request: { - /// The term to search for. - pub search_term: &'a str, - - /// The maximum number of results to return. - /// - /// Defaults to 10. - #[serde(default = "default_limit", skip_serializing_if = "is_default_limit")] - pub limit: UInt, - - /// Language tag to determine the collation to use for the (case-insensitive) search. - /// - /// See [MDN] for the syntax. - /// - /// [MDN]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language#Syntax - #[ruma_api(header = ACCEPT_LANGUAGE)] - pub language: Option, - } - - response: { - /// Ordered by rank and then whether or not profile info is available. - pub results: Vec, - - /// Indicates if the result list has been truncated by the limit. - pub limited: bool, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given search term. - pub fn new(search_term: &'a str) -> Self { - Self { search_term, limit: default_limit(), language: None } - } -} - -impl Response { - /// Creates a new `Response` with the given results and limited flag - pub fn new(results: Vec, limited: bool) -> Self { - Self { results, limited } - } -} - -fn default_limit() -> UInt { - uint!(10) -} - -fn is_default_limit(limit: &UInt) -> bool { - limit == &default_limit() -} - -/// User data as result of a search. -#[derive(Clone, Debug, Deserialize, Serialize)] -#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] -pub struct User { - /// The user's matrix user ID. - pub user_id: Box, - - /// The display name of the user, if one exists. - #[serde(skip_serializing_if = "Option::is_none")] - pub display_name: Option, - - /// The avatar url, as an MXC, if one exists. - /// - /// If you activate the `compat` feature, this field being an empty string in JSON will result - /// in `None` here during deserialization. - #[serde(skip_serializing_if = "Option::is_none")] - #[cfg_attr( - feature = "compat", - serde(default, deserialize_with = "ruma_serde::empty_string_as_none") - )] - pub avatar_url: Option>, -} - -impl User { - /// Create a new `User` with the given `UserId`. - pub fn new(user_id: Box) -> Self { - Self { user_id, display_name: None, avatar_url: None } - } -} diff --git a/crates/ruma-client-api/src/r0/voip/get_turn_server_info.rs b/crates/ruma-client-api/src/r0/voip/get_turn_server_info.rs deleted file mode 100644 index cf44626f..00000000 --- a/crates/ruma-client-api/src/r0/voip/get_turn_server_info.rs +++ /dev/null @@ -1,52 +0,0 @@ -//! [GET /_matrix/client/r0/voip/turnServer](https://matrix.org/docs/spec/client_server/r0.6.1#get-matrix-client-r0-voip-turnserver) - -use std::time::Duration; - -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "Get credentials for the client to use when initiating VoIP calls.", - method: GET, - name: "turn_server_info", - r0_path: "/_matrix/client/r0/voip/turnServer", - stable_path: "/_matrix/client/v3/voip/turnServer", - rate_limited: true, - authentication: AccessToken, - added: 1.0, - } - - #[derive(Default)] - request: {} - - response: { - /// The username to use. - pub username: String, - - /// The password to use. - pub password: String, - - /// A list of TURN URIs. - pub uris: Vec, - - /// The time-to-live in seconds. - #[serde(with = "ruma_serde::duration::secs")] - pub ttl: Duration, - } - - error: crate::Error -} - -impl Request { - /// Creates an empty `Request`. - pub fn new() -> Self { - Self {} - } -} - -impl Response { - /// Creates a new `Response` with the given username, password, TURN URIs and time-to-live. - pub fn new(username: String, password: String, uris: Vec, ttl: Duration) -> Self { - Self { username, password, uris, ttl } - } -} diff --git a/crates/ruma-client-api/src/r0/read_marker.rs b/crates/ruma-client-api/src/read_marker.rs similarity index 100% rename from crates/ruma-client-api/src/r0/read_marker.rs rename to crates/ruma-client-api/src/read_marker.rs diff --git a/crates/ruma-client-api/src/read_marker/set_read_marker.rs b/crates/ruma-client-api/src/read_marker/set_read_marker.rs new file mode 100644 index 00000000..dfdaadae --- /dev/null +++ b/crates/ruma-client-api/src/read_marker/set_read_marker.rs @@ -0,0 +1,61 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/read_markers` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidread_markers + + use ruma_api::ruma_api; + use ruma_identifiers::{EventId, RoomId}; + + ruma_api! { + metadata: { + description: "Sets the position of the read marker for a given room, and optionally the read receipt's location.", + method: POST, + name: "set_read_marker", + r0_path: "/_matrix/client/r0/rooms/:room_id/read_markers", + stable_path: "/_matrix/client/v3/rooms/:room_id/read_markers", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room ID to set the read marker in for the user. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The event ID the read marker should be located at. + /// + /// The event MUST belong to the room. + #[serde(rename = "m.fully_read")] + pub fully_read: &'a EventId, + + /// The event ID to set the read receipt location at. + /// + /// This is equivalent to calling the create_read_receipt endpoint and is provided here to + /// save that extra call. + #[serde(rename = "m.read", skip_serializing_if = "Option::is_none")] + pub read_receipt: Option<&'a EventId>, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID and fully read event ID. + pub fn new(room_id: &'a RoomId, fully_read: &'a EventId) -> Self { + Self { room_id, fully_read, read_receipt: None } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0/receipt.rs b/crates/ruma-client-api/src/receipt.rs similarity index 100% rename from crates/ruma-client-api/src/r0/receipt.rs rename to crates/ruma-client-api/src/receipt.rs diff --git a/crates/ruma-client-api/src/receipt/create_receipt.rs b/crates/ruma-client-api/src/receipt/create_receipt.rs new file mode 100644 index 00000000..ac6b6cd0 --- /dev/null +++ b/crates/ruma-client-api/src/receipt/create_receipt.rs @@ -0,0 +1,57 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/receipt/{receiptType}/{eventId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidreceiptreceipttypeeventid + + use ruma_api::ruma_api; + use ruma_common::receipt::ReceiptType; + use ruma_identifiers::{EventId, RoomId}; + + ruma_api! { + metadata: { + description: "Send a receipt event to a room.", + method: POST, + name: "create_receipt", + r0_path: "/_matrix/client/r0/rooms/:room_id/receipt/:receipt_type/:event_id", + stable_path: "/_matrix/client/v3/rooms/:room_id/receipt/:receipt_type/:event_id", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room in which to send the event. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The type of receipt to send. + #[ruma_api(path)] + pub receipt_type: ReceiptType, + + /// The event ID to acknowledge up to. + #[ruma_api(path)] + pub event_id: &'a EventId, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID, receipt type and event ID. + pub fn new(room_id: &'a RoomId, receipt_type: ReceiptType, event_id: &'a EventId) -> Self { + Self { room_id, receipt_type, event_id } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/r0/redact.rs b/crates/ruma-client-api/src/redact.rs similarity index 100% rename from crates/ruma-client-api/src/r0/redact.rs rename to crates/ruma-client-api/src/redact.rs diff --git a/crates/ruma-client-api/src/redact/redact_event.rs b/crates/ruma-client-api/src/redact/redact_event.rs new file mode 100644 index 00000000..513fa8b8 --- /dev/null +++ b/crates/ruma-client-api/src/redact/redact_event.rs @@ -0,0 +1,65 @@ +//! `PUT /_matrix/client/*/rooms/{roomId}/redact/{eventId}/{txnId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidredacteventidtxnid + + use ruma_api::ruma_api; + use ruma_identifiers::{EventId, RoomId, TransactionId}; + + ruma_api! { + metadata: { + description: "Redact an event, stripping all information not critical to the event graph integrity.", + method: PUT, + name: "redact_event", + r0_path: "/_matrix/client/r0/rooms/:room_id/redact/:event_id/:txn_id", + stable_path: "/_matrix/client/v3/rooms/:room_id/redact/:event_id/:txn_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The ID of the room of the event to redact. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The ID of the event to redact. + #[ruma_api(path)] + pub event_id: &'a EventId, + + /// The transaction ID for this event. + /// + /// Clients should generate a unique ID; it will be used by the server to ensure idempotency + /// of requests. + #[ruma_api(path)] + pub txn_id: &'a TransactionId, + + /// The reason for the redaction. + #[serde(skip_serializing_if = "Option::is_none")] + pub reason: Option<&'a str>, + } + + response: { + /// The ID of the redacted event. + pub event_id: Box, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID, event ID and transaction ID. + pub fn new(room_id: &'a RoomId, event_id: &'a EventId, txn_id: &'a TransactionId) -> Self { + Self { room_id, event_id, txn_id, reason: None } + } + } + + impl Response { + /// Creates a new `Response` with the given event ID. + pub fn new(event_id: Box) -> Self { + Self { event_id } + } + } +} diff --git a/crates/ruma-client-api/src/r0/room.rs b/crates/ruma-client-api/src/room.rs similarity index 100% rename from crates/ruma-client-api/src/r0/room.rs rename to crates/ruma-client-api/src/room.rs diff --git a/crates/ruma-client-api/src/room/aliases.rs b/crates/ruma-client-api/src/room/aliases.rs new file mode 100644 index 00000000..73dd4372 --- /dev/null +++ b/crates/ruma-client-api/src/room/aliases.rs @@ -0,0 +1,50 @@ +//! `GET /_matrix/client/*/rooms/{roomId}/aliases` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidaliases + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomAliasId, RoomId}; + + ruma_api! { + metadata: { + description: "Get a list of aliases maintained by the local server for the given room.", + method: GET, + name: "aliases", + r0_path: "/_matrix/client/r0/rooms/:room_id/aliases", + stable_path: "/_matrix/client/v3/rooms/:room_id/aliases", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room ID to get aliases of. + #[ruma_api(path)] + pub room_id: &'a RoomId, + } + + response: { + /// The server's local aliases on the room. + pub aliases: Vec>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID. + pub fn new(room_id: &'a RoomId) -> Self { + Self { room_id } + } + } + + impl Response { + /// Creates a new `Response` with the given aliases. + pub fn new(aliases: Vec>) -> Self { + Self { aliases } + } + } +} diff --git a/crates/ruma-client-api/src/room/create_room.rs b/crates/ruma-client-api/src/room/create_room.rs new file mode 100644 index 00000000..10484382 --- /dev/null +++ b/crates/ruma-client-api/src/room/create_room.rs @@ -0,0 +1,212 @@ +//! `POST /_matrix/client/*/createRoom` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom + + use assign::assign; + use ruma_api::ruma_api; + use ruma_events::{ + room::{ + create::{PreviousRoom, RoomCreateEventContent, RoomType}, + power_levels::RoomPowerLevelsEventContent, + }, + AnyInitialStateEvent, + }; + use ruma_identifiers::{RoomId, RoomName, RoomVersionId, UserId}; + use ruma_serde::{Raw, StringEnum}; + use serde::{Deserialize, Serialize}; + + use crate::{ + membership::{IncomingInvite3pid, Invite3pid}, + room::Visibility, + PrivOwnedStr, + }; + + ruma_api! { + metadata: { + description: "Create a new room.", + method: POST, + name: "create_room", + r0_path: "/_matrix/client/r0/createRoom", + stable_path: "/_matrix/client/v3/createRoom", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: { + /// Extra keys to be added to the content of the `m.room.create`. + #[serde(default, skip_serializing_if = "Option::is_none")] + pub creation_content: Option>, + + /// List of state events to send to the new room. + /// + /// Takes precedence over events set by preset, but gets overridden by name and topic keys. + #[serde(default, skip_serializing_if = "<[_]>::is_empty")] + pub initial_state: &'a [Raw], + + /// A list of user IDs to invite to the room. + /// + /// This will tell the server to invite everyone in the list to the newly created room. + #[serde(default, skip_serializing_if = "<[_]>::is_empty")] + pub invite: &'a [Box], + + /// List of third party IDs of users to invite. + #[serde(default, skip_serializing_if = "<[_]>::is_empty")] + pub invite_3pid: &'a [Invite3pid<'a>], + + /// If set, this sets the `is_direct` flag on room invites. + #[serde(default, skip_serializing_if = "ruma_serde::is_default")] + pub is_direct: bool, + + /// If this is included, an `m.room.name` event will be sent into the room to indicate the + /// name of the room. + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option<&'a RoomName>, + + /// Power level content to override in the default power level event. + #[serde(skip_serializing_if = "Option::is_none")] + pub power_level_content_override: Option>, + + /// Convenience parameter for setting various default state events based on a preset. + #[serde(skip_serializing_if = "Option::is_none")] + pub preset: Option, + + /// The desired room alias local part. + #[serde(skip_serializing_if = "Option::is_none")] + pub room_alias_name: Option<&'a str>, + + /// Room version to set for the room. + /// + /// Defaults to homeserver's default if not specified. + #[serde(skip_serializing_if = "Option::is_none")] + pub room_version: Option<&'a RoomVersionId>, + + /// If this is included, an `m.room.topic` event will be sent into the room to indicate + /// the topic for the room. + #[serde(skip_serializing_if = "Option::is_none")] + pub topic: Option<&'a str>, + + /// A public visibility indicates that the room will be shown in the published room list. + /// + /// A private visibility will hide the room from the published room list. Defaults to + /// `Private`. + #[serde(default, skip_serializing_if = "ruma_serde::is_default")] + pub visibility: Visibility, + } + + response: { + /// The created room's ID. + pub room_id: Box, + } + + error: crate::Error + } + + impl Request<'_> { + /// Creates a new `Request` will all-default parameters. + pub fn new() -> Self { + Default::default() + } + } + + impl Response { + /// Creates a new `Response` with the given room id. + pub fn new(room_id: Box) -> Self { + Self { room_id } + } + } + + /// Extra options to be added to the `m.room.create` event. + /// + /// This is the same as the event content struct for `m.room.create`, but without some fields + /// that servers are supposed to ignore. + #[derive(Clone, Debug, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct CreationContent { + /// Whether users on other servers can join this room. + /// + /// Defaults to `true` if key does not exist. + #[serde( + rename = "m.federate", + default = "ruma_serde::default_true", + skip_serializing_if = "ruma_serde::is_true" + )] + pub federate: bool, + + /// A reference to the room this room replaces, if the previous room was upgraded. + #[serde(skip_serializing_if = "Option::is_none")] + pub predecessor: Option, + + /// The room type. + /// + /// This is currently only used for spaces. + #[serde(skip_serializing_if = "Option::is_none", rename = "type")] + pub room_type: Option, + } + + impl CreationContent { + /// Creates a new `CreationContent` with all fields defaulted. + pub fn new() -> Self { + Self { federate: true, predecessor: None, room_type: None } + } + + /// Given a `CreationContent` and the other fields that a homeserver has to fill, construct + /// a `RoomCreateEventContent`. + pub fn into_event_content( + self, + creator: Box, + room_version: RoomVersionId, + ) -> RoomCreateEventContent { + assign!(RoomCreateEventContent::new(creator), { + federate: self.federate, + room_version: room_version, + predecessor: self.predecessor, + room_type: self.room_type + }) + } + + /// Returns whether all fields have their default value. + pub fn is_empty(&self) -> bool { + self.federate && self.predecessor.is_none() && self.room_type.is_none() + } + } + + impl Default for CreationContent { + fn default() -> Self { + Self::new() + } + } + + /// A convenience parameter for setting a few default state events. + /// + /// This type can hold an arbitrary string. To check for formats that are not available as a + /// documented variant here, use its string representation, obtained through `.as_str()`. + #[derive(Clone, Debug, PartialEq, Eq, StringEnum)] + #[ruma_enum(rename_all = "snake_case")] + #[non_exhaustive] + pub enum RoomPreset { + /// `join_rules` is set to `invite` and `history_visibility` is set to `shared`. + PrivateChat, + + /// `join_rules` is set to `public` and `history_visibility` is set to `shared`. + PublicChat, + + /// Same as `PrivateChat`, but all initial invitees get the same power level as the + /// creator. + TrustedPrivateChat, + + #[doc(hidden)] + _Custom(PrivOwnedStr), + } + + impl RoomPreset { + /// Creates a string slice from this `RoomPreset`. + pub fn as_str(&self) -> &str { + self.as_ref() + } + } +} diff --git a/crates/ruma-client-api/src/room/get_room_event.rs b/crates/ruma-client-api/src/room/get_room_event.rs new file mode 100644 index 00000000..f01c80f3 --- /dev/null +++ b/crates/ruma-client-api/src/room/get_room_event.rs @@ -0,0 +1,57 @@ +//! `GET /_matrix/client/*/rooms/{roomId}/event/{eventId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomideventeventid + + use ruma_api::ruma_api; + use ruma_events::AnyRoomEvent; + use ruma_identifiers::{EventId, RoomId}; + use ruma_serde::Raw; + + ruma_api! { + metadata: { + description: "Get a single event based on roomId/eventId", + method: GET, + name: "get_room_event", + r0_path: "/_matrix/client/r0/rooms/:room_id/event/:event_id", + stable_path: "/_matrix/client/v3/rooms/:room_id/event/:event_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The ID of the room the event is in. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The ID of the event. + #[ruma_api(path)] + pub event_id: &'a EventId, + } + + response: { + /// Arbitrary JSON of the event body. + #[ruma_api(body)] + pub event: Raw, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID and event ID. + pub fn new(room_id: &'a RoomId, event_id: &'a EventId) -> Self { + Self { room_id, event_id } + } + } + + impl Response { + /// Creates a new `Response` with the given event. + pub fn new(event: Raw) -> Self { + Self { event } + } + } +} diff --git a/crates/ruma-client-api/src/room/report_content.rs b/crates/ruma-client-api/src/room/report_content.rs new file mode 100644 index 00000000..89df0962 --- /dev/null +++ b/crates/ruma-client-api/src/room/report_content.rs @@ -0,0 +1,66 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/report/{eventId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidreporteventid + + use js_int::Int; + use ruma_api::ruma_api; + use ruma_identifiers::{EventId, RoomId}; + + ruma_api! { + metadata: { + description: "Report content as inappropriate.", + method: POST, + name: "report_content", + r0_path: "/_matrix/client/r0/rooms/:room_id/report/:event_id", + stable_path: "/_matrix/client/v3/rooms/:room_id/report/:event_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// Room in which the event to be reported is located. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// Event to report. + #[ruma_api(path)] + pub event_id: &'a EventId, + + /// Integer between -100 and 0 rating offensivness. + pub score: Int, + + /// Reason to report content. + /// + /// May be blank. + pub reason: &'a str, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID, event ID, score and reason. + pub fn new( + room_id: &'a RoomId, + event_id: &'a EventId, + score: Int, + reason: &'a str, + ) -> Self { + Self { room_id, event_id, score, reason } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/room/upgrade_room.rs b/crates/ruma-client-api/src/room/upgrade_room.rs new file mode 100644 index 00000000..d03b72a9 --- /dev/null +++ b/crates/ruma-client-api/src/room/upgrade_room.rs @@ -0,0 +1,53 @@ +//! `POST /_matrix/client/*/rooms/{roomId}/upgrade` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidupgrade + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomId, RoomVersionId}; + + ruma_api! { + metadata: { + description: "Upgrades a room to a particular version.", + method: POST, + name: "upgrade_room", + r0_path: "/_matrix/client/r0/rooms/:room_id/upgrade", + stable_path: "/_matrix/client/v3/rooms/:room_id/upgrade", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// ID of the room to be upgraded. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// New version for the room. + pub new_version: &'a RoomVersionId, + } + + response: { + /// ID of the new room. + pub replacement_room: Box, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID and new room version. + pub fn new(room_id: &'a RoomId, new_version: &'a RoomVersionId) -> Self { + Self { room_id, new_version } + } + } + + impl Response { + /// Creates a new `Response` with the given room ID. + pub fn new(replacement_room: Box) -> Self { + Self { replacement_room } + } + } +} diff --git a/crates/ruma-client-api/src/r0/search.rs b/crates/ruma-client-api/src/search.rs similarity index 100% rename from crates/ruma-client-api/src/r0/search.rs rename to crates/ruma-client-api/src/search.rs diff --git a/crates/ruma-client-api/src/search/search_events.rs b/crates/ruma-client-api/src/search/search_events.rs new file mode 100644 index 00000000..e3e841c5 --- /dev/null +++ b/crates/ruma-client-api/src/search/search_events.rs @@ -0,0 +1,516 @@ +//! `POST /_matrix/client/*/search` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3search + + use std::collections::BTreeMap; + + use js_int::{uint, UInt}; + use ruma_api::ruma_api; + use ruma_events::{AnyRoomEvent, AnyStateEvent}; + use ruma_identifiers::{EventId, MxcUri, RoomId, UserId}; + use ruma_serde::{Outgoing, Raw, StringEnum}; + use serde::{Deserialize, Serialize}; + + use crate::{ + filter::{IncomingRoomEventFilter, RoomEventFilter}, + PrivOwnedStr, + }; + + ruma_api! { + metadata: { + description: "Search events.", + method: POST, + name: "search", + r0_path: "/_matrix/client/r0/search", + stable_path: "/_matrix/client/v3/search", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The point to return events from. + /// + /// If given, this should be a `next_batch` result from a previous call to this endpoint. + #[ruma_api(query)] + pub next_batch: Option<&'a str>, + + /// Describes which categories to search in and their criteria. + pub search_categories: Categories<'a>, + } + + response: { + /// A grouping of search results by category. + pub search_categories: ResultCategories, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given categories. + pub fn new(search_categories: Categories<'a>) -> Self { + Self { next_batch: None, search_categories } + } + } + + impl Response { + /// Creates a new `Response` with the given search results. + pub fn new(search_categories: ResultCategories) -> Self { + Self { search_categories } + } + } + + /// Categories of events that can be searched for. + #[derive(Clone, Debug, Default, Outgoing, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Categories<'a> { + /// Criteria for searching room events. + #[serde(skip_serializing_if = "Option::is_none")] + pub room_events: Option>, + } + + impl Categories<'_> { + /// Creates an empty `Categories`. + pub fn new() -> Self { + Default::default() + } + } + + /// Criteria for searching a category of events. + #[derive(Clone, Debug, Outgoing, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Criteria<'a> { + /// The string to search events for. + pub search_term: &'a str, + + /// The keys to search for. + /// + /// Defaults to all keys. + #[serde(skip_serializing_if = "Option::is_none")] + pub keys: Option<&'a [SearchKeys]>, + + /// A `Filter` to apply to the search. + #[serde(skip_serializing_if = "RoomEventFilter::is_empty")] + pub filter: RoomEventFilter<'a>, + + /// The order in which to search for results. + #[serde(skip_serializing_if = "Option::is_none")] + pub order_by: Option, + + /// Configures whether any context for the events returned are included in the response. + #[serde(default, skip_serializing_if = "EventContext::is_default")] + pub event_context: EventContext, + + /// Requests the server return the current state for each room returned. + #[serde(skip_serializing_if = "Option::is_none")] + pub include_state: Option, + + /// Requests that the server partitions the result set based on the provided list of keys. + #[serde(default, skip_serializing_if = "Groupings::is_empty")] + pub groupings: Groupings<'a>, + } + + impl<'a> Criteria<'a> { + /// Creates a new `Criteria` with the given search term. + pub fn new(search_term: &'a str) -> Self { + Self { + search_term, + keys: None, + filter: RoomEventFilter::default(), + order_by: None, + event_context: Default::default(), + include_state: None, + groupings: Default::default(), + } + } + } + + /// Configures whether any context for the events returned are included in the response. + #[derive(Clone, Debug, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct EventContext { + /// How many events before the result are returned. + #[serde( + default = "default_event_context_limit", + skip_serializing_if = "is_default_event_context_limit" + )] + pub before_limit: UInt, + + /// How many events after the result are returned. + #[serde( + default = "default_event_context_limit", + skip_serializing_if = "is_default_event_context_limit" + )] + pub after_limit: UInt, + + /// Requests that the server returns the historic profile information for the users that + /// sent the events that were returned. + #[serde(default, skip_serializing_if = "ruma_serde::is_default")] + pub include_profile: bool, + } + + fn default_event_context_limit() -> UInt { + uint!(5) + } + + #[allow(clippy::trivially_copy_pass_by_ref)] + fn is_default_event_context_limit(val: &UInt) -> bool { + *val == default_event_context_limit() + } + + impl EventContext { + /// Creates an `EventContext` with all-default values. + pub fn new() -> Self { + Self { + before_limit: default_event_context_limit(), + after_limit: default_event_context_limit(), + include_profile: false, + } + } + + /// Returns whether all fields have their default value. + pub fn is_default(&self) -> bool { + self.before_limit == default_event_context_limit() + && self.after_limit == default_event_context_limit() + && !self.include_profile + } + } + + impl Default for EventContext { + fn default() -> Self { + Self::new() + } + } + + /// Context for search results, if requested. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct EventContextResult { + /// Pagination token for the end of the chunk. + #[serde(skip_serializing_if = "Option::is_none")] + pub end: Option, + + /// Events just after the result. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events_after: Vec>, + + /// Events just before the result. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events_before: Vec>, + + /// The historic profile information of the users that sent the events returned. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub profile_info: BTreeMap, UserProfile>, + + /// Pagination token for the start of the chunk. + #[serde(skip_serializing_if = "Option::is_none")] + pub start: Option, + } + + impl EventContextResult { + /// Creates an empty `EventContextResult`. + pub fn new() -> Self { + Default::default() + } + + /// Returns whether all fields are `None` or an empty list. + pub fn is_empty(&self) -> bool { + self.end.is_none() + && self.events_after.is_empty() + && self.events_before.is_empty() + && self.profile_info.is_empty() + && self.start.is_none() + } + } + + /// A grouping for partitioning the result set. + #[derive(Clone, Default, Debug, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Grouping { + /// The key within events to use for this grouping. + pub key: Option, + } + + impl Grouping { + /// Creates an empty `Grouping`. + pub fn new() -> Self { + Default::default() + } + + /// Returns whether `key` is `None`. + pub fn is_empty(&self) -> bool { + self.key.is_none() + } + } + + /// The key within events to use for this grouping. + /// + /// This type can hold an arbitrary string. To check for formats that are not available as a + /// documented variant here, use its string representation, obtained through `.as_str()`. + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, StringEnum)] + #[ruma_enum(rename_all = "snake_case")] + #[non_exhaustive] + pub enum GroupingKey { + /// `room_id` + RoomId, + + /// `sender` + Sender, + + #[doc(hidden)] + _Custom(PrivOwnedStr), + } + + impl GroupingKey { + /// Creates a string slice from this `GroupingKey`. + pub fn as_str(&self) -> &str { + self.as_ref() + } + } + + /// Requests that the server partitions the result set based on the provided list of keys. + #[derive(Clone, Default, Debug, Outgoing, Serialize)] + #[incoming_derive(Default)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Groupings<'a> { + /// List of groups to request. + #[serde(default, skip_serializing_if = "<[_]>::is_empty")] + pub group_by: &'a [Grouping], + } + + impl Groupings<'_> { + /// Creates an empty `Groupings`. + pub fn new() -> Self { + Default::default() + } + + /// Returns `true` if all fields are empty. + pub fn is_empty(&self) -> bool { + self.group_by.is_empty() + } + } + + /// The keys to search for. + /// + /// This type can hold an arbitrary string. To check for formats that are not available as a + /// documented variant here, use its string representation, obtained through `.as_str()`. + #[derive(Clone, Debug, PartialEq, Eq, StringEnum)] + #[non_exhaustive] + pub enum SearchKeys { + /// content.body + #[ruma_enum(rename = "content.body")] + ContentBody, + + /// content.name + #[ruma_enum(rename = "content.name")] + ContentName, + + /// content.topic + #[ruma_enum(rename = "content.topic")] + ContentTopic, + + #[doc(hidden)] + _Custom(PrivOwnedStr), + } + + impl SearchKeys { + /// Creates a string slice from this `SearchKeys`. + pub fn as_str(&self) -> &str { + self.as_ref() + } + } + + /// The order in which to search for results. + #[derive(Clone, Debug, PartialEq, Eq, StringEnum)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[ruma_enum(rename_all = "snake_case")] + pub enum OrderBy { + /// Prioritize recent events. + Recent, + + /// Prioritize events by a numerical ranking of how closely they matched the search + /// criteria. + Rank, + + #[doc(hidden)] + _Custom(PrivOwnedStr), + } + + /// Categories of events that can be searched for. + #[derive(Clone, Default, Debug, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct ResultCategories { + /// Room event results. + #[serde(default, skip_serializing_if = "ResultRoomEvents::is_empty")] + pub room_events: ResultRoomEvents, + } + + impl ResultCategories { + /// Creates an empty `ResultCategories`. + pub fn new() -> Self { + Default::default() + } + } + + /// Categories of events that can be searched for. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct ResultRoomEvents { + /// An approximate count of the total number of results found. + #[serde(skip_serializing_if = "Option::is_none")] + pub count: Option, + + /// Any groups that were requested. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub groups: BTreeMap, ResultGroup>>, + + /// Token that can be used to get the next batch of results, by passing as the `next_batch` + /// parameter to the next call. + /// + /// If this field is absent, there are no more results. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_batch: Option, + + /// List of results in the requested order. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub results: Vec, + + /// The current state for every room in the results. + /// + /// This is included if the request had the `include_state` key set with a value of `true`. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub state: BTreeMap, Vec>>, + + /// List of words which should be highlighted, useful for stemming which may + /// change the query terms. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub highlights: Vec, + } + + impl ResultRoomEvents { + /// Creates an empty `ResultRoomEvents`. + pub fn new() -> Self { + Default::default() + } + + /// Returns `true` if all fields are empty / `None`. + pub fn is_empty(&self) -> bool { + self.count.is_none() + && self.groups.is_empty() + && self.next_batch.is_none() + && self.results.is_empty() + && self.state.is_empty() + && self.highlights.is_empty() + } + } + + /// A grouping of results, if requested. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct ResultGroup { + /// Token that can be used to get the next batch of results in the group, by passing as the + /// `next_batch` parameter to the next call. + /// + /// If this field is absent, there are no more results in this group. + #[serde(skip_serializing_if = "Option::is_none")] + pub next_batch: Option, + + /// Key that can be used to order different groups. + #[serde(skip_serializing_if = "Option::is_none")] + pub order: Option, + + /// Which results are in this group. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub results: Vec>, + } + + impl ResultGroup { + /// Creates an empty `ResultGroup`. + pub fn new() -> Self { + Default::default() + } + + /// Returns `true` if all fields are empty / `None`. + pub fn is_empty(&self) -> bool { + self.next_batch.is_none() && self.order.is_none() && self.results.is_empty() + } + } + + /// A search result. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct SearchResult { + /// Context for result, if requested. + #[serde(skip_serializing_if = "EventContextResult::is_empty")] + pub context: EventContextResult, + + /// A number that describes how closely this result matches the search. + /// + /// Higher is closer. + #[serde(skip_serializing_if = "Option::is_none")] + pub rank: Option, + + /// The event that matched. + #[serde(skip_serializing_if = "Option::is_none")] + pub result: Option>, + } + + impl SearchResult { + /// Creates an empty `SearchResult`. + pub fn new() -> Self { + Default::default() + } + + /// Returns `true` if all fields are empty / `None`. + pub fn is_empty(&self) -> bool { + self.context.is_empty() && self.rank.is_none() && self.result.is_none() + } + } + + /// A user profile. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct UserProfile { + /// The user's avatar URL, if set. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will + /// result in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub avatar_url: Option>, + + /// The user's display name, if set. + #[serde(skip_serializing_if = "Option::is_none")] + pub displayname: Option, + } + + impl UserProfile { + /// Creates an empty `UserProfile`. + pub fn new() -> Self { + Default::default() + } + + /// Returns `true` if all fields are `None`. + pub fn is_empty(&self) -> bool { + self.avatar_url.is_none() && self.displayname.is_none() + } + } + + /// Represents either a room or user ID for returning grouped search results. + #[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)] + #[allow(clippy::exhaustive_enums)] + pub enum RoomIdOrUserId { + /// Represents a room ID. + RoomId(Box), + + /// Represents a user ID. + UserId(Box), + } +} diff --git a/crates/ruma-client-api/src/r0/server.rs b/crates/ruma-client-api/src/server.rs similarity index 100% rename from crates/ruma-client-api/src/r0/server.rs rename to crates/ruma-client-api/src/server.rs diff --git a/crates/ruma-client-api/src/server/get_user_info.rs b/crates/ruma-client-api/src/server/get_user_info.rs new file mode 100644 index 00000000..c17fb0fb --- /dev/null +++ b/crates/ruma-client-api/src/server/get_user_info.rs @@ -0,0 +1,113 @@ +//! `GET /_matrix/client/*/admin/whois/{userId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3adminwhoisuserid + + use std::collections::BTreeMap; + + use ruma_api::ruma_api; + use ruma_common::MilliSecondsSinceUnixEpoch; + use ruma_identifiers::UserId; + use serde::{Deserialize, Serialize}; + + ruma_api! { + metadata: { + description: "Get information about a particular user.", + method: GET, + name: "get_user_info", + r0_path: "/_matrix/client/r0/admin/whois/:user_id", + stable_path: "/_matrix/client/v3/admin/whois/:user_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The user to look up. + #[ruma_api(path)] + pub user_id: &'a UserId, + } + + #[derive(Default)] + response: { + /// The Matrix user ID of the user. + #[serde(skip_serializing_if = "Option::is_none")] + pub user_id: Option>, + + /// A map of the user's device identifiers to information about that device. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub devices: BTreeMap, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user id. + pub fn new(user_id: &'a UserId) -> Self { + Self { user_id } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Default::default() + } + } + + /// Information about a user's device. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct DeviceInfo { + /// A list of user sessions on this device. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub sessions: Vec, + } + + impl DeviceInfo { + /// Create a new `DeviceInfo` with no sessions. + pub fn new() -> Self { + Self::default() + } + } + + /// Information about a user session. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct SessionInfo { + /// A list of connections in this session. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub connections: Vec, + } + + impl SessionInfo { + /// Create a new `SessionInfo` with no connections. + pub fn new() -> Self { + Self::default() + } + } + + /// Information about a connection in a user session. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct ConnectionInfo { + /// Most recently seen IP address of the session. + pub ip: Option, + + /// Time when that the session was last active. + pub last_seen: Option, + + /// User agent string last seen in the session. + pub user_agent: Option, + } + + impl ConnectionInfo { + /// Create a new `ConnectionInfo` with all fields set to `None`. + pub fn new() -> Self { + Self::default() + } + } +} diff --git a/crates/ruma-client-api/src/session.rs b/crates/ruma-client-api/src/session.rs index c0ac50bb..f2f60d2d 100644 --- a/crates/ruma-client-api/src/session.rs +++ b/crates/ruma-client-api/src/session.rs @@ -1,3 +1,9 @@ //! Endpoints for user session management. +pub mod get_login_types; +pub mod login; +pub mod login_fallback; +pub mod logout; +pub mod logout_all; +pub mod sso_login; pub mod sso_login_with_provider; diff --git a/crates/ruma-client-api/src/session/get_login_types.rs b/crates/ruma-client-api/src/session/get_login_types.rs new file mode 100644 index 00000000..96f473f6 --- /dev/null +++ b/crates/ruma-client-api/src/session/get_login_types.rs @@ -0,0 +1,434 @@ +//! `GET /_matrix/client/*/login` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3login + + use std::borrow::Cow; + + use ruma_api::ruma_api; + use ruma_identifiers::MxcUri; + use ruma_serde::{JsonObject, StringEnum}; + use serde::{de::DeserializeOwned, Deserialize, Serialize}; + use serde_json::Value as JsonValue; + + use crate::PrivOwnedStr; + + ruma_api! { + metadata: { + description: "Gets the homeserver's supported login types to authenticate users. Clients should pick one of these and supply it as the type when logging in.", + method: GET, + name: "get_login_types", + r0_path: "/_matrix/client/r0/login", + stable_path: "/_matrix/client/v3/login", + rate_limited: true, + authentication: None, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// The homeserver's supported login types. + pub flows: Vec, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given login types. + pub fn new(flows: Vec) -> Self { + Self { flows } + } + } + + /// An authentication mechanism. + #[derive(Clone, Debug, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[serde(untagged)] + pub enum LoginType { + /// A password is supplied to authenticate. + Password(PasswordLoginType), + + /// Token-based login. + Token(TokenLoginType), + + /// SSO-based login. + Sso(SsoLoginType), + + /// Custom login type. + #[doc(hidden)] + _Custom(CustomLoginType), + } + + impl LoginType { + /// Creates a new `LoginType` with the given `login_type` string and data. + /// + /// Prefer to use the public variants of `LoginType` where possible; this constructor is + /// meant be used for unsupported login types only and does not allow setting + /// arbitrary data for supported ones. + pub fn new(login_type: &str, data: JsonObject) -> serde_json::Result { + fn from_json_object(obj: JsonObject) -> serde_json::Result { + serde_json::from_value(JsonValue::Object(obj)) + } + + Ok(match login_type { + "m.login.password" => Self::Password(from_json_object(data)?), + "m.login.token" => Self::Token(from_json_object(data)?), + "m.login.sso" => Self::Sso(from_json_object(data)?), + _ => Self::_Custom(CustomLoginType { type_: login_type.to_owned(), data }), + }) + } + + /// Returns a reference to the `login_type` string. + pub fn login_type(&self) -> &str { + match self { + Self::Password(_) => "m.login.password", + Self::Token(_) => "m.login.token", + Self::Sso(_) => "m.login.sso", + Self::_Custom(c) => &c.type_, + } + } + + /// Returns the associated data. + /// + /// Prefer to use the public variants of `LoginType` where possible; this method is meant to + /// be used for unsupported login types only. + pub fn data(&self) -> Cow<'_, JsonObject> { + fn serialize(obj: &T) -> JsonObject { + match serde_json::to_value(obj).expect("login type serialization to succeed") { + JsonValue::Object(obj) => obj, + _ => panic!("all login types must serialize to objects"), + } + } + + match self { + Self::Password(d) => Cow::Owned(serialize(d)), + Self::Token(d) => Cow::Owned(serialize(d)), + Self::Sso(d) => Cow::Owned(serialize(d)), + Self::_Custom(c) => Cow::Borrowed(&c.data), + } + } + } + + /// The payload for password login. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[serde(tag = "type", rename = "m.login.password")] + pub struct PasswordLoginType {} + + impl PasswordLoginType { + /// Creates a new `PasswordLoginType`. + pub fn new() -> Self { + Self {} + } + } + + /// The payload for token-based login. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[serde(tag = "type", rename = "m.login.token")] + pub struct TokenLoginType {} + + impl TokenLoginType { + /// Creates a new `PasswordLoginType`. + pub fn new() -> Self { + Self {} + } + } + + /// The payload for SSO login. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[serde(tag = "type", rename = "m.login.sso")] + pub struct SsoLoginType { + /// The identity provider choices. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub identity_providers: Vec, + } + + impl SsoLoginType { + /// Creates a new `PasswordLoginType`. + pub fn new() -> Self { + Self::default() + } + } + + /// An SSO login identity provider. + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[derive(Clone, Debug, Deserialize, Serialize)] + pub struct IdentityProvider { + /// The ID of the provider. + pub id: String, + + /// The display name of the provider. + pub name: String, + + /// The icon for the provider. + pub icon: Option>, + + /// The brand identifier for the provider. + pub brand: Option, + } + + impl IdentityProvider { + /// Creates an `IdentityProvider` with the given `id` and `name`. + pub fn new(id: String, name: String) -> Self { + Self { id, name, icon: None, brand: None } + } + } + + /// An SSO login identity provider brand identifier. + /// + /// The predefined ones can be found in the matrix-doc repo in a [separate + /// document][matrix-doc]. To use a custom brand string, simply use + /// `IdentityProviderBrand::from("custom-brand")` or `"custom-brand".into()` (if the type is + /// known from the surrounding context). + /// + /// [matrix-doc]: https://github.com/matrix-org/matrix-doc/blob/v1.1/informal/idp-brands.md + #[derive(Clone, Debug, PartialEq, Eq, StringEnum)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub enum IdentityProviderBrand { + /// The [Apple] brand. + /// + /// [Apple]: https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple/overview/buttons/ + #[ruma_enum(rename = "apple")] + Apple, + + /// The [Facebook](https://developers.facebook.com/docs/facebook-login/web/login-button/) brand. + #[ruma_enum(rename = "facebook")] + Facebook, + + /// The [GitHub](https://github.com/logos) brand. + #[ruma_enum(rename = "github")] + GitHub, + + /// The [GitLab](https://about.gitlab.com/press/press-kit/) brand. + #[ruma_enum(rename = "gitlab")] + GitLab, + + /// The [Google](https://developers.google.com/identity/branding-guidelines) brand. + #[ruma_enum(rename = "google")] + Google, + + /// The [Twitter] brand. + /// + /// [Twitter]: https://developer.twitter.com/en/docs/authentication/guides/log-in-with-twitter#tab1 + #[ruma_enum(rename = "twitter")] + Twitter, + + /// A custom brand. + #[doc(hidden)] + _Custom(PrivOwnedStr), + } + + /// A custom login payload. + #[doc(hidden)] + #[derive(Clone, Debug, Deserialize, Serialize)] + #[allow(clippy::exhaustive_structs)] + pub struct CustomLoginType { + /// A custom type + /// + /// This field is named `type_` instead of `type` because the latter is a reserved + /// keyword in Rust. + #[serde(rename = "type")] + pub type_: String, + + /// Remaining type content + #[serde(flatten)] + pub data: JsonObject, + } + + mod login_type_serde { + use ruma_serde::from_raw_json_value; + use serde::{de, Deserialize}; + use serde_json::value::RawValue as RawJsonValue; + + use super::LoginType; + + /// Helper struct to determine the type from a `serde_json::value::RawValue` + #[derive(Debug, Deserialize)] + struct LoginTypeDeHelper { + /// The login type field + #[serde(rename = "type")] + type_: String, + } + + impl<'de> Deserialize<'de> for LoginType { + fn deserialize(deserializer: D) -> Result + where + D: de::Deserializer<'de>, + { + let json = Box::::deserialize(deserializer)?; + let LoginTypeDeHelper { type_ } = from_raw_json_value(&json)?; + + Ok(match type_.as_ref() { + "m.login.password" => Self::Password(from_raw_json_value(&json)?), + "m.login.token" => Self::Token(from_raw_json_value(&json)?), + "m.login.sso" => Self::Sso(from_raw_json_value(&json)?), + _ => Self::_Custom(from_raw_json_value(&json)?), + }) + } + } + } + + #[cfg(test)] + mod tests { + use matches::assert_matches; + use serde::{Deserialize, Serialize}; + use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; + + use super::{ + CustomLoginType, IdentityProvider, IdentityProviderBrand, LoginType, PasswordLoginType, + SsoLoginType, TokenLoginType, + }; + + #[derive(Debug, Deserialize, Serialize)] + struct Wrapper { + pub flows: Vec, + } + + #[test] + fn deserialize_password_login_type() { + assert_matches!( + from_json_value::(json!({ + "flows": [ + { "type": "m.login.password" } + ], + })), + Ok(Wrapper { flows }) + if flows.len() == 1 + && matches!(flows[0], LoginType::Password(PasswordLoginType {})) + ); + } + + #[test] + fn deserialize_custom_login_type() { + assert_matches!( + from_json_value::(json!({ + "flows": [ + { + "type": "io.ruma.custom", + "color": "green", + } + ], + })), + Ok(Wrapper { flows }) + if flows.len() == 1 + && matches!( + &flows[0], + LoginType::_Custom(CustomLoginType { type_, data }) + if type_ == "io.ruma.custom" + && data == json!({ "color": "green" }).as_object().unwrap() + ) + ); + } + + #[test] + fn deserialize_sso_login_type() { + let mut wrapper = from_json_value::(json!({ + "flows": [ + { + "type": "m.login.sso", + "identity_providers": [ + { + "id": "oidc-gitlab", + "name": "GitLab", + "icon": "mxc://localhost/gitlab-icon", + "brand": "gitlab" + }, + { + "id": "custom", + "name": "Custom", + } + ] + } + ], + })) + .unwrap(); + + let flow = wrapper.flows.pop(); + assert_matches!(wrapper.flows.as_slice(), []); + + let mut identity_providers = match flow { + Some(LoginType::Sso(SsoLoginType { identity_providers })) => identity_providers, + _ => panic!("unexpected enum variant: {:?}", flow), + }; + + let provider = identity_providers.pop(); + assert_matches!( + provider, + Some(IdentityProvider { + id, + name, + icon: None, + brand: None, + }) if id == "custom" + && name == "Custom" + ); + + let provider = identity_providers.pop(); + assert_matches!( + provider, + Some(IdentityProvider { + id, + name, + icon: Some(icon), + brand: Some(IdentityProviderBrand::GitLab), + }) if id == "oidc-gitlab" + && name == "GitLab" + && icon == "mxc://localhost/gitlab-icon" + ); + } + + #[test] + fn serialize_sso_login_type() { + let wrapper = to_json_value(Wrapper { + flows: vec![ + LoginType::Token(TokenLoginType {}), + LoginType::Sso(SsoLoginType { + identity_providers: vec![IdentityProvider { + id: "oidc-github".into(), + name: "GitHub".into(), + icon: Some("mxc://localhost/github-icon".into()), + brand: Some(IdentityProviderBrand::GitHub), + }], + }), + ], + }) + .unwrap(); + + assert_eq!( + wrapper, + json!({ + "flows": [ + { + "type": "m.login.token" + }, + { + "type": "m.login.sso", + "identity_providers": [ + { + "id": "oidc-github", + "name": "GitHub", + "icon": "mxc://localhost/github-icon", + "brand": "github" + }, + ] + } + ], + }) + ); + } + } +} diff --git a/crates/ruma-client-api/src/session/login.rs b/crates/ruma-client-api/src/session/login.rs new file mode 100644 index 00000000..5df7b0f3 --- /dev/null +++ b/crates/ruma-client-api/src/session/login.rs @@ -0,0 +1,394 @@ +//! `POST /_matrix/client/*/login` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3login + + use ruma_api::ruma_api; + use ruma_identifiers::{DeviceId, ServerName, UserId}; + use ruma_serde::{JsonObject, Outgoing}; + use serde::{ + de::{self, DeserializeOwned}, + Deserialize, Deserializer, Serialize, + }; + use serde_json::Value as JsonValue; + + use crate::uiaa::{IncomingUserIdentifier, UserIdentifier}; + + ruma_api! { + metadata: { + description: "Login to the homeserver.", + method: POST, + name: "login", + r0_path: "/_matrix/client/r0/login", + stable_path: "/_matrix/client/v3/login", + rate_limited: true, + authentication: None, + added: 1.0, + } + + request: { + /// The authentication mechanism. + #[serde(flatten)] + pub login_info: LoginInfo<'a>, + + /// ID of the client device + #[serde(skip_serializing_if = "Option::is_none")] + pub device_id: Option<&'a DeviceId>, + + /// A display name to assign to the newly-created device. + /// + /// Ignored if `device_id` corresponds to a known device. + #[serde(skip_serializing_if = "Option::is_none")] + pub initial_device_display_name: Option<&'a str>, + } + + response: { + /// The fully-qualified Matrix ID that has been registered. + pub user_id: Box, + + /// An access token for the account. + pub access_token: String, + + /// The hostname of the homeserver on which the account has been registered. + /// + /// Deprecated: Clients should instead use the `user_id.server_name()` + /// method if they require it. + #[serde(skip_serializing_if = "Option::is_none")] + pub home_server: Option>, + + /// ID of the logged-in device. + /// + /// Will be the same as the corresponding parameter in the request, if one was + /// specified. + pub device_id: Box, + + /// Client configuration provided by the server. + /// + /// If present, clients SHOULD use the provided object to reconfigure themselves. + #[serde(skip_serializing_if = "Option::is_none")] + pub well_known: Option, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given login info. + pub fn new(login_info: LoginInfo<'a>) -> Self { + Self { login_info, device_id: None, initial_device_display_name: None } + } + } + + impl Response { + /// Creates a new `Response` with the given user ID, access token and device ID. + pub fn new(user_id: Box, access_token: String, device_id: Box) -> Self { + Self { user_id, access_token, home_server: None, device_id, well_known: None } + } + } + + /// The authentication mechanism. + /// + /// To construct the custom `LoginInfo` variant you first have to construct + /// [`IncomingLoginInfo::new`] and then call [`IncomingLoginInfo::to_outgoing`] on it. + #[derive(Clone, Debug, Outgoing, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[incoming_derive(!Deserialize)] + #[serde(untagged)] + pub enum LoginInfo<'a> { + /// An identifier and password are supplied to authenticate. + Password(Password<'a>), + + /// Token-based login. + Token(Token<'a>), + + #[doc(hidden)] + _Custom(CustomLoginInfo<'a>), + } + + impl IncomingLoginInfo { + /// Creates a new `IncomingLoginInfo` with the given `login_type` string, session and data. + /// + /// Prefer to use the public variants of `IncomingLoginInfo` where possible; this + /// constructor is meant be used for unsupported authentication mechanisms only and + /// does not allow setting arbitrary data for supported ones. + /// + /// # Errors + /// + /// Returns an error if the `login_type` is known and serialization of `data` to the + /// corresponding `IncomingLoginInfo` variant fails. + pub fn new(login_type: &str, data: JsonObject) -> serde_json::Result { + Ok(match login_type { + "m.login.password" => { + Self::Password(serde_json::from_value(JsonValue::Object(data))?) + } + "m.login.token" => Self::Token(serde_json::from_value(JsonValue::Object(data))?), + _ => Self::_Custom(IncomingCustomLoginInfo { + login_type: login_type.into(), + extra: data, + }), + }) + } + + /// Convert `IncomingLoginInfo` to `LoginInfo`. + pub fn to_outgoing(&self) -> LoginInfo<'_> { + match self { + Self::Password(a) => LoginInfo::Password(a.to_outgoing()), + Self::Token(a) => LoginInfo::Token(a.to_outgoing()), + Self::_Custom(a) => LoginInfo::_Custom(CustomLoginInfo { + login_type: &a.login_type, + extra: &a.extra, + }), + } + } + } + + impl<'de> Deserialize<'de> for IncomingLoginInfo { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + fn from_json_value(val: JsonValue) -> Result { + serde_json::from_value(val).map_err(E::custom) + } + + // FIXME: Would be better to use serde_json::value::RawValue, but that would require + // implementing Deserialize manually for Request, bc. `#[serde(flatten)]` breaks things. + let json = JsonValue::deserialize(deserializer)?; + + let login_type = + json["type"].as_str().ok_or_else(|| de::Error::missing_field("type"))?; + match login_type { + "m.login.password" => from_json_value(json).map(Self::Password), + "m.login.token" => from_json_value(json).map(Self::Token), + _ => from_json_value(json).map(Self::_Custom), + } + } + } + + /// An identifier and password to supply as authentication. + #[derive(Clone, Debug, Outgoing, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[serde(tag = "type", rename = "m.login.password")] + pub struct Password<'a> { + /// Identification information for the user. + pub identifier: UserIdentifier<'a>, + + /// The password. + pub password: &'a str, + } + + impl<'a> Password<'a> { + /// Creates a new `Password` with the given identifier and password. + pub fn new(identifier: UserIdentifier<'a>, password: &'a str) -> Self { + Self { identifier, password } + } + } + + impl IncomingPassword { + /// Convert `IncomingPassword` to `Password`. + fn to_outgoing(&self) -> Password<'_> { + Password { identifier: self.identifier.to_outgoing(), password: &self.password } + } + } + + /// A token to supply as authentication. + #[derive(Clone, Debug, Outgoing, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[serde(tag = "type", rename = "m.login.token")] + pub struct Token<'a> { + /// The token. + pub token: &'a str, + } + + impl<'a> Token<'a> { + /// Creates a new `Token` with the given token. + pub fn new(token: &'a str) -> Self { + Self { token } + } + } + + impl IncomingToken { + /// Convert `IncomingToken` to `Token`. + fn to_outgoing(&self) -> Token<'_> { + Token { token: &self.token } + } + } + + #[doc(hidden)] + #[derive(Clone, Debug, Serialize)] + #[non_exhaustive] + pub struct CustomLoginInfo<'a> { + #[serde(rename = "type")] + login_type: &'a str, + #[serde(flatten)] + extra: &'a JsonObject, + } + + #[doc(hidden)] + #[derive(Clone, Debug, Deserialize)] + #[non_exhaustive] + pub struct IncomingCustomLoginInfo { + #[serde(rename = "type")] + login_type: String, + #[serde(flatten)] + extra: JsonObject, + } + + impl Outgoing for CustomLoginInfo<'_> { + type Incoming = IncomingCustomLoginInfo; + } + + /// Client configuration provided by the server. + #[derive(Clone, Debug, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct DiscoveryInfo { + /// Information about the homeserver to connect to. + #[serde(rename = "m.homeserver")] + pub homeserver: HomeserverInfo, + + /// Information about the identity server to connect to. + #[serde(rename = "m.identity_server")] + pub identity_server: Option, + } + + impl DiscoveryInfo { + /// Create a new `DiscoveryInfo` with the given homeserver. + pub fn new(homeserver: HomeserverInfo) -> Self { + Self { homeserver, identity_server: None } + } + } + + /// Information about the homeserver to connect to. + #[derive(Clone, Debug, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct HomeserverInfo { + /// The base URL for the homeserver for client-server connections. + pub base_url: String, + } + + impl HomeserverInfo { + /// Create a new `HomeserverInfo` with the given base url. + pub fn new(base_url: String) -> Self { + Self { base_url } + } + } + + /// Information about the identity server to connect to. + #[derive(Clone, Debug, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct IdentityServerInfo { + /// The base URL for the identity server for client-server connections. + pub base_url: String, + } + + impl IdentityServerInfo { + /// Create a new `IdentityServerInfo` with the given base url. + pub fn new(base_url: String) -> Self { + Self { base_url } + } + } + + #[cfg(test)] + mod tests { + use matches::assert_matches; + use serde_json::{from_value as from_json_value, json}; + + use super::{IncomingLoginInfo, IncomingPassword, IncomingToken}; + use crate::uiaa::IncomingUserIdentifier; + + #[test] + fn deserialize_login_type() { + assert_matches!( + from_json_value(json!({ + "type": "m.login.password", + "identifier": { + "type": "m.id.user", + "user": "cheeky_monkey" + }, + "password": "ilovebananas" + })) + .unwrap(), + IncomingLoginInfo::Password(IncomingPassword { identifier: IncomingUserIdentifier::MatrixId(user), password }) + if user == "cheeky_monkey" && password == "ilovebananas" + ); + + assert_matches!( + from_json_value(json!({ + "type": "m.login.token", + "token": "1234567890abcdef" + })) + .unwrap(), + IncomingLoginInfo::Token(IncomingToken { token }) + if token == "1234567890abcdef" + ); + } + + #[test] + #[cfg(feature = "client")] + fn serialize_login_request_body() { + use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken}; + use ruma_common::thirdparty::Medium; + use serde_json::Value as JsonValue; + + use super::{LoginInfo, Password, Request, Token}; + use crate::uiaa::UserIdentifier; + + let req: http::Request> = Request { + login_info: LoginInfo::Token(Token { token: "0xdeadbeef" }), + device_id: None, + initial_device_display_name: Some("test"), + } + .try_into_http_request( + "https://homeserver.tld", + SendAccessToken::None, + &[MatrixVersion::V1_1], + ) + .unwrap(); + + let req_body_value: JsonValue = serde_json::from_slice(req.body()).unwrap(); + assert_eq!( + req_body_value, + json!({ + "type": "m.login.token", + "token": "0xdeadbeef", + "initial_device_display_name": "test", + }) + ); + + let req: http::Request> = Request { + login_info: LoginInfo::Password(Password { + identifier: UserIdentifier::ThirdPartyId { + address: "hello@example.com", + medium: Medium::Email, + }, + password: "deadbeef", + }), + device_id: None, + initial_device_display_name: Some("test"), + } + .try_into_http_request( + "https://homeserver.tld", + SendAccessToken::None, + &[MatrixVersion::V1_1], + ) + .unwrap(); + + let req_body_value: JsonValue = serde_json::from_slice(req.body()).unwrap(); + assert_eq!( + req_body_value, + json!({ + "identifier": { + "type": "m.id.thirdparty", + "medium": "email", + "address": "hello@example.com" + }, + "type": "m.login.password", + "password": "deadbeef", + "initial_device_display_name": "test", + }) + ); + } + } +} diff --git a/crates/ruma-client-api/src/r0/session/login_fallback.rs b/crates/ruma-client-api/src/session/login_fallback.rs similarity index 90% rename from crates/ruma-client-api/src/r0/session/login_fallback.rs rename to crates/ruma-client-api/src/session/login_fallback.rs index d1c74d74..77d172a6 100644 --- a/crates/ruma-client-api/src/r0/session/login_fallback.rs +++ b/crates/ruma-client-api/src/session/login_fallback.rs @@ -1,4 +1,6 @@ -//! [GET /_matrix/static/client/login/](https://spec.matrix.org/unstable/client-server-api/#login-fallback) +//! `GET /_matrix/static/client/login/` (fallback, [spec]) +//! +//! [spec]: https://spec.matrix.org/v1.2/client-server-api/#login-fallback use ruma_api::ruma_api; use ruma_identifiers::DeviceId; diff --git a/crates/ruma-client-api/src/session/logout.rs b/crates/ruma-client-api/src/session/logout.rs new file mode 100644 index 00000000..df4c876b --- /dev/null +++ b/crates/ruma-client-api/src/session/logout.rs @@ -0,0 +1,44 @@ +//! `POST /_matrix/client/*/logout` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3logout + + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "Log out of the homeserver.", + method: POST, + name: "logout", + r0_path: "/_matrix/client/r0/logout", + stable_path: "/_matrix/client/v3/logout", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/session/logout_all.rs b/crates/ruma-client-api/src/session/logout_all.rs new file mode 100644 index 00000000..71d0e0fb --- /dev/null +++ b/crates/ruma-client-api/src/session/logout_all.rs @@ -0,0 +1,44 @@ +//! `POST /_matrix/client/*/logout/all` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3logoutall + + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "Invalidates all access tokens for a user, so that they can no longer be used for authorization.", + method: POST, + name: "logout_all", + r0_path: "/_matrix/client/r0/logout/all", + stable_path: "/_matrix/client/v3/logout/all", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/session/sso_login.rs b/crates/ruma-client-api/src/session/sso_login.rs new file mode 100644 index 00000000..dd8395c9 --- /dev/null +++ b/crates/ruma-client-api/src/session/sso_login.rs @@ -0,0 +1,75 @@ +//! `GET /_matrix/client/*/login/sso/redirect` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3loginssoredirect + + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "", + method: GET, + name: "sso_login", + unstable_path: "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect", + stable_path: "/_matrix/client/v3/login/sso/redirect", + rate_limited: false, + authentication: None, + added: 1.1, + } + + request: { + /// URL to which the homeserver should return the user after completing + /// authentication with the SSO identity provider. + #[ruma_api(query)] + #[serde(rename = "redirectUrl")] + pub redirect_url: &'a str, + } + + response: { + /// Redirect URL to the SSO identity provider. + #[ruma_api(header = LOCATION)] + pub location: String, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given redirect URL. + pub fn new(redirect_url: &'a str) -> Self { + Self { redirect_url } + } + } + + impl Response { + /// Creates a new `Response` with the given SSO URL. + pub fn new(location: String) -> Self { + Self { location } + } + } + + #[cfg(all(test, feature = "client"))] + mod tests { + use ruma_api::{MatrixVersion, OutgoingRequest, SendAccessToken}; + + use super::Request; + + #[test] + fn serialize_sso_login_request_uri() { + let req: http::Request> = Request { redirect_url: "https://example.com/sso" } + .try_into_http_request( + "https://homeserver.tld", + SendAccessToken::None, + &[MatrixVersion::V1_1], + ) + .unwrap(); + + assert_eq!( + req.uri().to_string(), + "https://homeserver.tld/_matrix/client/v3/login/sso/redirect?redirectUrl=https%3A%2F%2Fexample.com%2Fsso" + ); + } + } +} diff --git a/crates/ruma-client-api/src/session/sso_login_with_provider.rs b/crates/ruma-client-api/src/session/sso_login_with_provider.rs index e7c21d5f..fd2bfbcd 100644 --- a/crates/ruma-client-api/src/session/sso_login_with_provider.rs +++ b/crates/ruma-client-api/src/session/sso_login_with_provider.rs @@ -1,3 +1,81 @@ +//! `GET /_matrix/client/*/login/sso/redirect/{idpId}` +//! //! Get the SSO login identity provider url. -pub mod v3; +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3loginssoredirectidpid + + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "Get the SSO login identity provider url.", + method: GET, + name: "sso_login_with_provider", + unstable_path: "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect/:idp_id", + stable_path: "/_matrix/client/v3/login/sso/redirect/:idp_id", + rate_limited: false, + authentication: None, + added: 1.1, + } + + request: { + /// The ID of the provider to use for SSO login. + #[ruma_api(path)] + pub idp_id: &'a str, + + /// URL to which the homeserver should return the user after completing + /// authentication with the SSO identity provider. + #[ruma_api(query)] + #[serde(rename = "redirectUrl")] + pub redirect_url: &'a str, + } + + response: { + /// Redirect URL to the SSO identity provider. + #[ruma_api(header = LOCATION)] + pub location: String, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given identity provider ID and redirect URL. + pub fn new(idp_id: &'a str, redirect_url: &'a str) -> Self { + Self { idp_id, redirect_url } + } + } + + impl Response { + /// Creates a new `Response` with the given SSO URL. + pub fn new(location: String) -> Self { + Self { location } + } + } + + #[cfg(all(test, feature = "client"))] + mod tests { + use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken}; + + use super::Request; + + #[test] + fn serialize_sso_login_with_provider_request_uri() { + let req = Request { idp_id: "provider", redirect_url: "https://example.com/sso" } + .try_into_http_request::>( + "https://homeserver.tld", + SendAccessToken::None, + &[MatrixVersion::V1_1], + ) + .unwrap(); + + assert_eq!( + req.uri().to_string(), + "https://homeserver.tld/_matrix/client/v3/login/sso/redirect/provider?redirectUrl=https%3A%2F%2Fexample.com%2Fsso" + ); + } + } +} diff --git a/crates/ruma-client-api/src/session/sso_login_with_provider/v3.rs b/crates/ruma-client-api/src/session/sso_login_with_provider/v3.rs deleted file mode 100644 index 5529ed00..00000000 --- a/crates/ruma-client-api/src/session/sso_login_with_provider/v3.rs +++ /dev/null @@ -1,73 +0,0 @@ -//! [GET /_matrix/client/v3/login/sso/redirect/{idp_id}](https://spec.matrix.org/v1.1/client-server-api/#get_matrixclientv3loginssoredirectidpid) - -use ruma_api::ruma_api; - -ruma_api! { - metadata: { - description: "Get the SSO login identity provider url.", - method: GET, - name: "sso_login_with_provider", - unstable_path: "/_matrix/client/unstable/org.matrix.msc2858/login/sso/redirect/:idp_id", - stable_path: "/_matrix/client/v3/login/sso/redirect/:idp_id", - rate_limited: false, - authentication: None, - added: 1.1, - } - - request: { - /// The ID of the provider to use for SSO login. - #[ruma_api(path)] - pub idp_id: &'a str, - - /// URL to which the homeserver should return the user after completing - /// authentication with the SSO identity provider. - #[ruma_api(query)] - #[serde(rename = "redirectUrl")] - pub redirect_url: &'a str, - } - - response: { - /// Redirect URL to the SSO identity provider. - #[ruma_api(header = LOCATION)] - pub location: String, - } - - error: crate::Error -} - -impl<'a> Request<'a> { - /// Creates a new `Request` with the given identity provider ID and redirect URL. - pub fn new(idp_id: &'a str, redirect_url: &'a str) -> Self { - Self { idp_id, redirect_url } - } -} - -impl Response { - /// Creates a new `Response` with the given SSO URL. - pub fn new(location: String) -> Self { - Self { location } - } -} - -#[cfg(all(test, feature = "client"))] -mod tests { - use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken}; - - use super::Request; - - #[test] - fn serialize_sso_login_with_provider_request_uri() { - let req = Request { idp_id: "provider", redirect_url: "https://example.com/sso" } - .try_into_http_request::>( - "https://homeserver.tld", - SendAccessToken::None, - &[MatrixVersion::V1_1], - ) - .unwrap(); - - assert_eq!( - req.uri().to_string(), - "https://homeserver.tld/_matrix/client/v3/login/sso/redirect/provider?redirectUrl=https%3A%2F%2Fexample.com%2Fsso" - ); - } -} diff --git a/crates/ruma-client-api/src/r0/state.rs b/crates/ruma-client-api/src/state.rs similarity index 100% rename from crates/ruma-client-api/src/r0/state.rs rename to crates/ruma-client-api/src/state.rs diff --git a/crates/ruma-client-api/src/state/get_state_events.rs b/crates/ruma-client-api/src/state/get_state_events.rs new file mode 100644 index 00000000..2722c181 --- /dev/null +++ b/crates/ruma-client-api/src/state/get_state_events.rs @@ -0,0 +1,57 @@ +//! `GET /_matrix/client/*/rooms/{roomId}/state` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidstate + + use ruma_api::ruma_api; + use ruma_events::AnyStateEvent; + use ruma_identifiers::RoomId; + use ruma_serde::Raw; + + ruma_api! { + metadata: { + description: "Get state events for a room.", + method: GET, + name: "get_state_events", + r0_path: "/_matrix/client/r0/rooms/:room_id/state", + stable_path: "/_matrix/client/v3/rooms/:room_id/state", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The room to look up the state for. + #[ruma_api(path)] + pub room_id: &'a RoomId, + } + + response: { + /// If the user is a member of the room this will be the current state of the room as a + /// list of events. + /// + /// If the user has left the room then this will be the state of the room when they left as + /// a list of events. + #[ruma_api(body)] + pub room_state: Vec>, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID. + pub fn new(room_id: &'a RoomId) -> Self { + Self { room_id } + } + } + + impl Response { + /// Creates a new `Response` with the given room state. + pub fn new(room_state: Vec>) -> Self { + Self { room_state } + } + } +} diff --git a/crates/ruma-client-api/src/state/get_state_events_for_key.rs b/crates/ruma-client-api/src/state/get_state_events_for_key.rs new file mode 100644 index 00000000..baf70467 --- /dev/null +++ b/crates/ruma-client-api/src/state/get_state_events_for_key.rs @@ -0,0 +1,168 @@ +//! `GET /_matrix/client/*/rooms/{roomId}/state/{eventType}/{stateKey}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidstateeventtypestatekey + + use ruma_api::ruma_api; + use ruma_events::{AnyStateEventContent, EventType}; + use ruma_identifiers::RoomId; + use ruma_serde::{Outgoing, Raw}; + + ruma_api! { + metadata: { + description: "Get state events associated with a given key.", + method: GET, + name: "get_state_events_for_key", + r0_path: "/_matrix/client/r0/rooms/:room_id/state/:event_type/:state_key", + stable_path: "/_matrix/client/v3/rooms/:room_id/state/:event_type/:state_key", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + response: { + /// The content of the state event. + /// + /// Since the inner type of the `Raw` does not implement `Deserialize`, you need to use + /// `ruma_events::RawExt` to deserialize it. + #[ruma_api(body)] + pub content: Raw, + } + + error: crate::Error + } + + /// Data for a request to the `get_state_events_for_key` API endpoint. + /// + /// Get state events associated with a given key. + #[derive(Clone, Debug, Outgoing)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[incoming_derive(!Deserialize)] + pub struct Request<'a> { + /// The room to look up the state for. + pub room_id: &'a RoomId, + + /// The type of state to look up. + pub event_type: EventType, + + /// The key of the state to look up. + pub state_key: &'a str, + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room ID, event type and state key. + pub fn new(room_id: &'a RoomId, event_type: EventType, state_key: &'a str) -> Self { + Self { room_id, event_type, state_key } + } + } + + impl Response { + /// Creates a new `Response` with the given content. + pub fn new(content: Raw) -> Self { + Self { content } + } + } + + #[cfg(feature = "client")] + impl<'a> ruma_api::OutgoingRequest for Request<'a> { + type EndpointError = crate::Error; + type IncomingResponse = ::Incoming; + + const METADATA: ruma_api::Metadata = METADATA; + + fn try_into_http_request( + self, + base_url: &str, + access_token: ruma_api::SendAccessToken<'_>, + considering_versions: &'_ [ruma_api::MatrixVersion], + ) -> Result, ruma_api::error::IntoHttpError> { + use std::borrow::Cow; + + use http::header; + use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; + + let room_id_percent = utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC); + let event_type_percent = + utf8_percent_encode(self.event_type.as_str(), NON_ALPHANUMERIC); + + let mut url = format!( + "{}{}", + base_url.strip_suffix('/').unwrap_or(base_url), + ruma_api::select_path( + considering_versions, + &METADATA, + None, + Some(format_args!( + "/_matrix/client/r0/rooms/{}/state/{}", + room_id_percent, event_type_percent + )), + None, + )? + ); + + if !self.state_key.is_empty() { + url.push('/'); + url.push_str(&Cow::from(utf8_percent_encode(self.state_key, NON_ALPHANUMERIC))); + } + + http::Request::builder() + .method(http::Method::GET) + .uri(url) + .header(header::CONTENT_TYPE, "application/json") + .header( + header::AUTHORIZATION, + format!( + "Bearer {}", + access_token + .get_required_for_endpoint() + .ok_or(ruma_api::error::IntoHttpError::NeedsAuthentication)?, + ), + ) + .body(T::default()) + .map_err(Into::into) + } + } + + #[cfg(feature = "server")] + impl ruma_api::IncomingRequest for IncomingRequest { + type EndpointError = crate::Error; + type OutgoingResponse = Response; + + const METADATA: ruma_api::Metadata = METADATA; + + fn try_from_http_request( + _request: http::Request, + path_args: &[S], + ) -> Result + where + B: AsRef<[u8]>, + S: AsRef, + { + // FIXME: find a way to make this if-else collapse with serde recognizing trailing + // Option + let (room_id, event_type, state_key): (Box, EventType, String) = + if path_args.len() == 3 { + serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::< + _, + serde::de::value::Error, + >::new( + path_args.iter().map(::std::convert::AsRef::as_ref), + ))? + } else { + let (a, b) = + serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::< + _, + serde::de::value::Error, + >::new( + path_args.iter().map(::std::convert::AsRef::as_ref), + ))?; + + (a, b, "".into()) + }; + + Ok(Self { room_id, event_type, state_key }) + } + } +} diff --git a/crates/ruma-client-api/src/state/send_state_event.rs b/crates/ruma-client-api/src/state/send_state_event.rs new file mode 100644 index 00000000..6bdc35db --- /dev/null +++ b/crates/ruma-client-api/src/state/send_state_event.rs @@ -0,0 +1,196 @@ +//! `PUT /_matrix/client/*/rooms/{roomId}/state/{eventType}/{stateKey}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidstateeventtypestatekey + + use ruma_api::ruma_api; + use ruma_events::{AnyStateEventContent, StateEventContent}; + use ruma_identifiers::{EventId, RoomId}; + use ruma_serde::{Outgoing, Raw}; + use serde_json::value::to_raw_value as to_raw_json_value; + + ruma_api! { + metadata: { + description: "Send a state event to a room associated with a given state key.", + method: PUT, + name: "send_state_event", + r0_path: "/_matrix/client/r0/rooms/:room_id/state/:event_type/:state_key", + stable_path: "/_matrix/client/v3/rooms/:room_id/state/:event_type/:state_key", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + response: { + /// A unique identifier for the event. + pub event_id: Box, + } + + error: crate::Error + } + + /// Data for a request to the `send_state_event` API endpoint. + /// + /// Send a state event to a room associated with a given state key. + #[derive(Clone, Debug, Outgoing)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[incoming_derive(!Deserialize)] + pub struct Request<'a> { + /// The room to set the state in. + pub room_id: &'a RoomId, + + /// The type of event to send. + pub event_type: &'a str, + + /// The state_key for the state to send. + pub state_key: &'a str, + + /// The event content to send. + pub body: Raw, + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room id, state key and event content. + /// + /// # Errors + /// + /// Since `Request` stores the request body in serialized form, this function can fail if + /// `T`s [`Serialize`][serde::Serialize] implementation can fail. + pub fn new( + room_id: &'a RoomId, + state_key: &'a str, + content: &'a T, + ) -> serde_json::Result { + Ok(Self { + room_id, + state_key, + event_type: content.event_type(), + body: Raw::from_json(to_raw_json_value(content)?), + }) + } + + /// Creates a new `Request` with the given room id, event type, state key and raw event + /// content. + pub fn new_raw( + room_id: &'a RoomId, + event_type: &'a str, + state_key: &'a str, + body: Raw, + ) -> Self { + Self { room_id, event_type, state_key, body } + } + } + + impl Response { + /// Creates a new `Response` with the given event id. + pub fn new(event_id: Box) -> Self { + Self { event_id } + } + } + + #[cfg(feature = "client")] + impl<'a> ruma_api::OutgoingRequest for Request<'a> { + type EndpointError = crate::Error; + type IncomingResponse = Response; + + const METADATA: ruma_api::Metadata = METADATA; + + fn try_into_http_request( + self, + base_url: &str, + access_token: ruma_api::SendAccessToken<'_>, + considering_versions: &'_ [ruma_api::MatrixVersion], + ) -> Result, ruma_api::error::IntoHttpError> { + use std::borrow::Cow; + + use http::header::{self, HeaderValue}; + use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC}; + + let room_id_percent = utf8_percent_encode(self.room_id.as_str(), NON_ALPHANUMERIC); + let event_type_percent = utf8_percent_encode(self.event_type, NON_ALPHANUMERIC); + + let mut url = format!( + "{}{}", + base_url.strip_suffix('/').unwrap_or(base_url), + ruma_api::select_path( + considering_versions, + &METADATA, + None, + Some(format_args!( + "/_matrix/client/r0/rooms/{}/state/{}", + room_id_percent, event_type_percent + )), + None, + )? + ); + + // Last URL segment is optional, that is why this trait impl is not generated. + if !self.state_key.is_empty() { + url.push('/'); + url.push_str(&Cow::from(utf8_percent_encode(self.state_key, NON_ALPHANUMERIC))); + } + + let http_request = http::Request::builder() + .method(http::Method::PUT) + .uri(url) + .header(header::CONTENT_TYPE, "application/json") + .header( + header::AUTHORIZATION, + HeaderValue::from_str(&format!( + "Bearer {}", + access_token + .get_required_for_endpoint() + .ok_or(ruma_api::error::IntoHttpError::NeedsAuthentication)? + ))?, + ) + .body(ruma_serde::json_to_buf(&self.body)?)?; + + Ok(http_request) + } + } + + #[cfg(feature = "server")] + impl ruma_api::IncomingRequest for IncomingRequest { + type EndpointError = crate::Error; + type OutgoingResponse = Response; + + const METADATA: ruma_api::Metadata = METADATA; + + fn try_from_http_request( + request: http::Request, + path_args: &[S], + ) -> Result + where + B: AsRef<[u8]>, + S: AsRef, + { + // FIXME: find a way to make this if-else collapse with serde recognizing trailing + // Option + let (room_id, event_type, state_key): (Box, String, String) = if path_args.len() + == 3 + { + serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::< + _, + serde::de::value::Error, + >::new( + path_args.iter().map(::std::convert::AsRef::as_ref), + ))? + } else { + let (a, b) = serde::Deserialize::deserialize(serde::de::value::SeqDeserializer::< + _, + serde::de::value::Error, + >::new( + path_args.iter().map(::std::convert::AsRef::as_ref), + ))?; + + (a, b, "".into()) + }; + + let body = serde_json::from_slice(request.body().as_ref())?; + + Ok(Self { room_id, event_type, state_key, body }) + } + } +} diff --git a/crates/ruma-client-api/src/r0/sync.rs b/crates/ruma-client-api/src/sync.rs similarity index 100% rename from crates/ruma-client-api/src/r0/sync.rs rename to crates/ruma-client-api/src/sync.rs diff --git a/crates/ruma-client-api/src/sync/sync_events.rs b/crates/ruma-client-api/src/sync/sync_events.rs new file mode 100644 index 00000000..04a59bc6 --- /dev/null +++ b/crates/ruma-client-api/src/sync/sync_events.rs @@ -0,0 +1,740 @@ +//! `GET /_matrix/client/*/sync` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3sync + + use std::{collections::BTreeMap, time::Duration}; + + use js_int::UInt; + use ruma_api::ruma_api; + use ruma_common::presence::PresenceState; + use ruma_events::{ + presence::PresenceEvent, AnyGlobalAccountDataEvent, AnyRoomAccountDataEvent, + AnyStrippedStateEvent, AnySyncEphemeralRoomEvent, AnySyncRoomEvent, AnySyncStateEvent, + AnyToDeviceEvent, + }; + use ruma_identifiers::{DeviceKeyAlgorithm, RoomId, UserId}; + use ruma_serde::{Outgoing, Raw}; + use serde::{Deserialize, Serialize}; + + use crate::filter::{FilterDefinition, IncomingFilterDefinition}; + + ruma_api! { + metadata: { + description: "Get all new events from all rooms since the last sync or a given point of time.", + method: GET, + name: "sync", + r0_path: "/_matrix/client/r0/sync", + stable_path: "/_matrix/client/v3/sync", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: { + /// A filter represented either as its full JSON definition or the ID of a saved filter. + #[serde(skip_serializing_if = "Option::is_none")] + #[ruma_api(query)] + pub filter: Option<&'a Filter<'a>>, + + /// A point in time to continue a sync from. + /// + /// Should be a token from the `next_batch` field of a previous `/sync` + /// request. + #[serde(skip_serializing_if = "Option::is_none")] + #[ruma_api(query)] + pub since: Option<&'a str>, + + /// Controls whether to include the full state for all rooms the user is a member of. + #[serde(default, skip_serializing_if = "ruma_serde::is_default")] + #[ruma_api(query)] + pub full_state: bool, + + /// Controls whether the client is automatically marked as online by polling this API. + /// + /// Defaults to `PresenceState::Online`. + #[serde(default, skip_serializing_if = "ruma_serde::is_default")] + #[ruma_api(query)] + pub set_presence: &'a PresenceState, + + /// The maximum time to poll in milliseconds before returning this request. + #[serde( + with = "ruma_serde::duration::opt_ms", + default, + skip_serializing_if = "Option::is_none", + )] + #[ruma_api(query)] + pub timeout: Option, + } + + response: { + /// The batch token to supply in the `since` param of the next `/sync` request. + pub next_batch: String, + + /// Updates to rooms. + #[serde(default, skip_serializing_if = "Rooms::is_empty")] + pub rooms: Rooms, + + /// Updates to the presence status of other users. + #[serde(default, skip_serializing_if = "Presence::is_empty")] + pub presence: Presence, + + /// The global private data created by this user. + #[serde(default, skip_serializing_if = "GlobalAccountData::is_empty")] + pub account_data: GlobalAccountData, + + /// Messages sent directly between devices. + #[serde(default, skip_serializing_if = "ToDevice::is_empty")] + pub to_device: ToDevice, + + /// Information on E2E device updates. + /// + /// Only present on an incremental sync. + #[serde(default, skip_serializing_if = "DeviceLists::is_empty")] + pub device_lists: DeviceLists, + + /// For each key algorithm, the number of unclaimed one-time keys + /// currently held on the server for a device. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub device_one_time_keys_count: BTreeMap, + + /// For each key algorithm, the number of unclaimed one-time keys + /// currently held on the server for a device. + /// + /// The presence of this field indicates that the server supports + /// fallback keys. + pub device_unused_fallback_key_types: Option>, + } + + error: crate::Error + } + + impl Request<'_> { + /// Creates an empty `Request`. + pub fn new() -> Self { + Default::default() + } + } + + impl Response { + /// Creates a new `Response` with the given batch token. + pub fn new(next_batch: String) -> Self { + Self { + next_batch, + rooms: Default::default(), + presence: Default::default(), + account_data: Default::default(), + to_device: Default::default(), + device_lists: Default::default(), + device_one_time_keys_count: BTreeMap::new(), + device_unused_fallback_key_types: None, + } + } + } + + /// A filter represented either as its full JSON definition or the ID of a saved filter. + #[derive(Clone, Debug, Outgoing, Serialize)] + #[allow(clippy::large_enum_variant)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + #[serde(untagged)] + pub enum Filter<'a> { + // The filter definition needs to be (de)serialized twice because it is a URL-encoded JSON + // string. Since #[ruma_api(query)] only does the latter and this is a very uncommon + // setup, we implement it through custom serde logic for this specific enum variant rather + // than adding another ruma_api attribute. + // + // On the deserialization side, because this is an enum with #[serde(untagged)], serde + // will try the variants in order (https://serde.rs/enum-representations.html). That means because + // FilterDefinition is the first variant, JSON decoding is attempted first which is almost + // functionally equivalent to looking at whether the first symbol is a '{' as the spec + // says. (there are probably some corner cases like leading whitespace) + /// A complete filter definition serialized to JSON. + #[serde(with = "ruma_serde::json_string")] + FilterDefinition(FilterDefinition<'a>), + + /// The ID of a filter saved on the server. + FilterId(&'a str), + } + + impl<'a> From> for Filter<'a> { + fn from(def: FilterDefinition<'a>) -> Self { + Self::FilterDefinition(def) + } + } + + impl<'a> From<&'a str> for Filter<'a> { + fn from(id: &'a str) -> Self { + Self::FilterId(id) + } + } + + /// Updates to rooms. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Rooms { + /// The rooms that the user has left or been banned from. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub leave: BTreeMap, LeftRoom>, + + /// The rooms that the user has joined. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub join: BTreeMap, JoinedRoom>, + + /// The rooms that the user has been invited to. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub invite: BTreeMap, InvitedRoom>, + + /// The rooms that the user has knocked on. + #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] + pub knock: BTreeMap, KnockedRoom>, + } + + impl Rooms { + /// Creates an empty `Rooms`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there is no update in any room. + pub fn is_empty(&self) -> bool { + self.leave.is_empty() && self.join.is_empty() && self.invite.is_empty() + } + } + + /// Historical updates to left rooms. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct LeftRoom { + /// The timeline of messages and state changes in the room up to the point when the user + /// left. + #[serde(default, skip_serializing_if = "Timeline::is_empty")] + pub timeline: Timeline, + + /// The state updates for the room up to the start of the timeline. + #[serde(default, skip_serializing_if = "State::is_empty")] + pub state: State, + + /// The private data that this user has attached to this room. + #[serde(default, skip_serializing_if = "RoomAccountData::is_empty")] + pub account_data: RoomAccountData, + } + + impl LeftRoom { + /// Creates an empty `LeftRoom`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are updates in the room. + pub fn is_empty(&self) -> bool { + self.timeline.is_empty() && self.state.is_empty() && self.account_data.is_empty() + } + } + + /// Updates to joined rooms. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct JoinedRoom { + /// Information about the room which clients may need to correctly render it + /// to users. + #[serde(default, skip_serializing_if = "RoomSummary::is_empty")] + pub summary: RoomSummary, + + /// Counts of unread notifications for this room. + #[serde(default, skip_serializing_if = "UnreadNotificationsCount::is_empty")] + pub unread_notifications: UnreadNotificationsCount, + + /// The timeline of messages and state changes in the room. + #[serde(default, skip_serializing_if = "Timeline::is_empty")] + pub timeline: Timeline, + + /// Updates to the state, between the time indicated by the `since` parameter, and the + /// start of the `timeline` (or all state up to the start of the `timeline`, if + /// `since` is not given, or `full_state` is true). + #[serde(default, skip_serializing_if = "State::is_empty")] + pub state: State, + + /// The private data that this user has attached to this room. + #[serde(default, skip_serializing_if = "RoomAccountData::is_empty")] + pub account_data: RoomAccountData, + + /// The ephemeral events in the room that aren't recorded in the timeline or state of the + /// room. + #[serde(default, skip_serializing_if = "Ephemeral::is_empty")] + pub ephemeral: Ephemeral, + } + + impl JoinedRoom { + /// Creates an empty `JoinedRoom`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no updates in the room. + pub fn is_empty(&self) -> bool { + self.summary.is_empty() + && self.unread_notifications.is_empty() + && self.timeline.is_empty() + && self.state.is_empty() + && self.account_data.is_empty() + && self.ephemeral.is_empty() + } + } + + /// Updates to knocked rooms. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct KnockedRoom { + /// The knock state. + pub knock_state: KnockState, + } + + /// A mapping from a key `events` to a list of `StrippedStateEvent`. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct KnockState { + /// The list of events. + pub events: Vec>, + } + + /// Unread notifications count. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct UnreadNotificationsCount { + /// The number of unread notifications for this room with the highlight flag set. + #[serde(skip_serializing_if = "Option::is_none")] + pub highlight_count: Option, + + /// The total number of unread notifications for this room. + #[serde(skip_serializing_if = "Option::is_none")] + pub notification_count: Option, + } + + impl UnreadNotificationsCount { + /// Creates an empty `UnreadNotificationsCount`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no notification count updates. + pub fn is_empty(&self) -> bool { + self.highlight_count.is_none() && self.notification_count.is_none() + } + } + + /// Events in the room. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Timeline { + /// True if the number of events returned was limited by the `limit` on the filter. + /// + /// Default to `false`. + #[serde(default, skip_serializing_if = "ruma_serde::is_default")] + pub limited: bool, + + /// A token that can be supplied to to the `from` parameter of the + /// `/rooms/{roomId}/messages` endpoint. + #[serde(skip_serializing_if = "Option::is_none")] + pub prev_batch: Option, + + /// A list of events. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events: Vec>, + } + + impl Timeline { + /// Creates an empty `Timeline`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no timeline updates. + pub fn is_empty(&self) -> bool { + self.events.is_empty() + } + } + + /// State events in the room. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct State { + /// A list of state events. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events: Vec>, + } + + impl State { + /// Creates an empty `State`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no state updates. + pub fn is_empty(&self) -> bool { + self.events.is_empty() + } + } + + /// The global private data created by this user. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct GlobalAccountData { + /// A list of events. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events: Vec>, + } + + impl GlobalAccountData { + /// Creates an empty `GlobalAccountData`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no global account data updates. + pub fn is_empty(&self) -> bool { + self.events.is_empty() + } + } + + /// The private data that this user has attached to this room. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct RoomAccountData { + /// A list of events. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events: Vec>, + } + + impl RoomAccountData { + /// Creates an empty `RoomAccountData`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no room account data updates. + pub fn is_empty(&self) -> bool { + self.events.is_empty() + } + } + + /// Ephemeral events not recorded in the timeline or state of the room. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Ephemeral { + /// A list of events. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events: Vec>, + } + + impl Ephemeral { + /// Creates an empty `Ephemeral`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no ephemeral event updates. + pub fn is_empty(&self) -> bool { + self.events.is_empty() + } + } + + /// Information about room for rendering to clients. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct RoomSummary { + /// Users which can be used to generate a room name if the room does not have one. + /// + /// Required if room name or canonical aliases are not set or empty. + #[serde(rename = "m.heroes", default, skip_serializing_if = "Vec::is_empty")] + pub heroes: Vec, + + /// Number of users whose membership status is `join`. + /// Required if field has changed since last sync; otherwise, it may be + /// omitted. + #[serde(rename = "m.joined_member_count", skip_serializing_if = "Option::is_none")] + pub joined_member_count: Option, + + /// Number of users whose membership status is `invite`. + /// Required if field has changed since last sync; otherwise, it may be + /// omitted. + #[serde(rename = "m.invited_member_count", skip_serializing_if = "Option::is_none")] + pub invited_member_count: Option, + } + + impl RoomSummary { + /// Creates an empty `RoomSummary`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no room summary updates. + pub fn is_empty(&self) -> bool { + self.heroes.is_empty() + && self.joined_member_count.is_none() + && self.invited_member_count.is_none() + } + } + + /// Updates to the rooms that the user has been invited to. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct InvitedRoom { + /// The state of a room that the user has been invited to. + #[serde(default, skip_serializing_if = "InviteState::is_empty")] + pub invite_state: InviteState, + } + + impl InvitedRoom { + /// Creates an empty `InvitedRoom`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no updates to this room. + pub fn is_empty(&self) -> bool { + self.invite_state.is_empty() + } + } + + /// The state of a room that the user has been invited to. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct InviteState { + /// A list of state events. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events: Vec>, + } + + impl InviteState { + /// Creates an empty `InviteState`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no state updates. + pub fn is_empty(&self) -> bool { + self.events.is_empty() + } + } + + /// Updates to the presence status of other users. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct Presence { + /// A list of events. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events: Vec>, + } + + impl Presence { + /// Creates an empty `Presence`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no presence updates. + pub fn is_empty(&self) -> bool { + self.events.is_empty() + } + } + + /// Messages sent directly between devices. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct ToDevice { + /// A list of to-device events. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub events: Vec>, + } + + impl ToDevice { + /// Creates an empty `ToDevice`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no to-device events. + pub fn is_empty(&self) -> bool { + self.events.is_empty() + } + } + + /// Information on E2E device updates. + #[derive(Clone, Debug, Default, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct DeviceLists { + /// List of users who have updated their device identity keys or who now + /// share an encrypted room with the client since the previous sync + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub changed: Vec>, + + /// List of users who no longer share encrypted rooms since the previous sync + /// response. + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub left: Vec>, + } + + impl DeviceLists { + /// Creates an empty `DeviceLists`. + pub fn new() -> Self { + Default::default() + } + + /// Returns true if there are no device list updates. + pub fn is_empty(&self) -> bool { + self.changed.is_empty() && self.left.is_empty() + } + } + + #[cfg(test)] + mod tests { + use assign::assign; + use matches::assert_matches; + use serde_json::{from_value as from_json_value, json, to_value as to_json_value}; + + use super::Timeline; + + #[test] + fn timeline_serde() { + let timeline = assign!(Timeline::new(), { limited: true }); + let timeline_serialized = json!({ "limited": true }); + assert_eq!(to_json_value(timeline).unwrap(), timeline_serialized); + + let timeline_deserialized = from_json_value(timeline_serialized); + assert_matches!(timeline_deserialized, Ok(Timeline { limited: true, .. })); + + let timeline_default = Timeline::default(); + assert_eq!(to_json_value(timeline_default).unwrap(), json!({})); + + let timeline_default_deserialized = from_json_value(json!({})); + assert_matches!(timeline_default_deserialized, Ok(Timeline { limited: false, .. })); + } + } + + #[cfg(all(test, feature = "client"))] + mod client_tests { + use std::time::Duration; + + use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken}; + + use super::{Filter, PresenceState, Request}; + + #[test] + fn serialize_all_params() { + let req: http::Request> = Request { + filter: Some(&Filter::FilterId("66696p746572")), + since: Some("s72594_4483_1934"), + full_state: true, + set_presence: &PresenceState::Offline, + timeout: Some(Duration::from_millis(30000)), + } + .try_into_http_request( + "https://homeserver.tld", + SendAccessToken::IfRequired("auth_tok"), + &[MatrixVersion::V1_1], + ) + .unwrap(); + + let uri = req.uri(); + let query = uri.query().unwrap(); + + assert_eq!(uri.path(), "/_matrix/client/v3/sync"); + assert!(query.contains("filter=66696p746572")); + assert!(query.contains("since=s72594_4483_1934")); + assert!(query.contains("full_state=true")); + assert!(query.contains("set_presence=offline")); + assert!(query.contains("timeout=30000")) + } + } + + #[cfg(all(test, feature = "server"))] + mod server_tests { + use std::time::Duration; + + use matches::assert_matches; + use ruma_api::IncomingRequest as _; + use ruma_common::presence::PresenceState; + + use super::{IncomingFilter, IncomingRequest}; + + #[test] + fn deserialize_all_query_params() { + let uri = http::Uri::builder() + .scheme("https") + .authority("matrix.org") + .path_and_query( + "/_matrix/client/r0/sync\ + ?filter=myfilter\ + &since=myts\ + &full_state=false\ + &set_presence=offline\ + &timeout=5000", + ) + .build() + .unwrap(); + + let req = IncomingRequest::try_from_http_request( + http::Request::builder().uri(uri).body(&[] as &[u8]).unwrap(), + &[] as &[String], + ) + .unwrap(); + + assert_matches!(req.filter, Some(IncomingFilter::FilterId(id)) if id == "myfilter"); + assert_eq!(req.since, Some("myts".into())); + assert!(!req.full_state); + assert_eq!(req.set_presence, PresenceState::Offline); + assert_eq!(req.timeout, Some(Duration::from_millis(5000))); + } + + #[test] + fn deserialize_no_query_params() { + let uri = http::Uri::builder() + .scheme("https") + .authority("matrix.org") + .path_and_query("/_matrix/client/r0/sync") + .build() + .unwrap(); + + let req = IncomingRequest::try_from_http_request( + http::Request::builder().uri(uri).body(&[] as &[u8]).unwrap(), + &[] as &[String], + ) + .unwrap(); + + assert_matches!(req.filter, None); + assert_eq!(req.since, None); + assert!(!req.full_state); + assert_eq!(req.set_presence, PresenceState::Online); + assert_eq!(req.timeout, None); + } + + #[test] + fn deserialize_some_query_params() { + let uri = http::Uri::builder() + .scheme("https") + .authority("matrix.org") + .path_and_query( + "/_matrix/client/r0/sync\ + ?filter=EOKFFmdZYF\ + &timeout=0", + ) + .build() + .unwrap(); + + let req = IncomingRequest::try_from_http_request( + http::Request::builder().uri(uri).body(&[] as &[u8]).unwrap(), + &[] as &[String], + ) + .unwrap(); + + assert_matches!(req.filter, Some(IncomingFilter::FilterId(id)) if id == "EOKFFmdZYF"); + assert_eq!(req.since, None); + assert!(!req.full_state); + assert_eq!(req.set_presence, PresenceState::Online); + assert_eq!(req.timeout, Some(Duration::from_millis(0))); + } + } +} diff --git a/crates/ruma-client-api/src/r0/tag.rs b/crates/ruma-client-api/src/tag.rs similarity index 100% rename from crates/ruma-client-api/src/r0/tag.rs rename to crates/ruma-client-api/src/tag.rs diff --git a/crates/ruma-client-api/src/tag/create_tag.rs b/crates/ruma-client-api/src/tag/create_tag.rs new file mode 100644 index 00000000..7455c8b8 --- /dev/null +++ b/crates/ruma-client-api/src/tag/create_tag.rs @@ -0,0 +1,66 @@ +//! `PUT /_matrix/client/*/user/{userId}/rooms/{roomId}/tags/{tag}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3useruseridroomsroomidtagstag + + use ruma_api::ruma_api; + use ruma_events::tag::TagInfo; + use ruma_identifiers::{RoomId, UserId}; + + ruma_api! { + metadata: { + description: "Add a new tag to a room.", + method: PUT, + name: "create_tag", + r0_path: "/_matrix/client/r0/user/:user_id/rooms/:room_id/tags/:tag", + stable_path: "/_matrix/client/v3/user/:user_id/rooms/:room_id/tags/:tag", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The ID of the user creating the tag. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// The room to tag. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The name of the tag to create. + #[ruma_api(path)] + pub tag: &'a str, + + /// Info about the tag. + #[ruma_api(body)] + pub tag_info: TagInfo, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID, room ID, tag and tag info. + pub fn new( + user_id: &'a UserId, + room_id: &'a RoomId, + tag: &'a str, + tag_info: TagInfo, + ) -> Self { + Self { user_id, room_id, tag, tag_info } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/tag/delete_tag.rs b/crates/ruma-client-api/src/tag/delete_tag.rs new file mode 100644 index 00000000..80b95dfd --- /dev/null +++ b/crates/ruma-client-api/src/tag/delete_tag.rs @@ -0,0 +1,56 @@ +//! `DELETE /_matrix/client/*/user/{userId}/rooms/{roomId}/tags/{tag}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3useruseridroomsroomidtagstag + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomId, UserId}; + + ruma_api! { + metadata: { + description: "Remove a tag from a room.", + method: DELETE, + name: "delete_tag", + r0_path: "/_matrix/client/r0/user/:user_id/rooms/:room_id/tags/:tag", + stable_path: "/_matrix/client/v3/user/:user_id/rooms/:room_id/tags/:tag", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The user whose tag will be deleted. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// The tagged room. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// The name of the tag to delete. + #[ruma_api(path)] + pub tag: &'a str, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID, room ID and tag + pub fn new(user_id: &'a UserId, room_id: &'a RoomId, tag: &'a str) -> Self { + Self { user_id, room_id, tag } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } +} diff --git a/crates/ruma-client-api/src/tag/get_tags.rs b/crates/ruma-client-api/src/tag/get_tags.rs new file mode 100644 index 00000000..9c584f98 --- /dev/null +++ b/crates/ruma-client-api/src/tag/get_tags.rs @@ -0,0 +1,91 @@ +//! `GET /_matrix/client/*/user/{userId}/rooms/{roomId}/tags` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3useruseridroomsroomidtags + + use ruma_api::ruma_api; + use ruma_events::tag::Tags; + use ruma_identifiers::{RoomId, UserId}; + + ruma_api! { + metadata: { + description: "Get the tags associated with a room.", + method: GET, + name: "get_tags", + r0_path: "/_matrix/client/r0/user/:user_id/rooms/:room_id/tags", + stable_path: "/_matrix/client/v3/user/:user_id/rooms/:room_id/tags", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The user whose tags will be retrieved. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// The room from which tags will be retrieved. + #[ruma_api(path)] + pub room_id: &'a RoomId, + } + + response: { + /// The user's tags for the room. + pub tags: Tags, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID and room ID. + pub fn new(user_id: &'a UserId, room_id: &'a RoomId) -> Self { + Self { user_id, room_id } + } + } + + impl Response { + /// Creates a new `Response` with the given tags. + pub fn new(tags: Tags) -> Self { + Self { tags } + } + } + + #[cfg(all(test, feature = "server"))] + mod server_tests { + use assign::assign; + use ruma_api::OutgoingResponse; + use ruma_events::tag::{TagInfo, Tags}; + use serde_json::json; + + use super::Response; + + #[test] + fn serializing_get_tags_response() { + let mut tags = Tags::new(); + tags.insert("m.favourite".into(), assign!(TagInfo::new(), { order: Some(0.25) })); + tags.insert("u.user_tag".into(), assign!(TagInfo::new(), { order: Some(0.11) })); + let response = Response { tags }; + + let http_response = response.try_into_http_response::>().unwrap(); + + let json_response: serde_json::Value = + serde_json::from_slice(http_response.body()).unwrap(); + assert_eq!( + json_response, + json!({ + "tags": { + "m.favourite": { + "order": 0.25, + }, + "u.user_tag": { + "order": 0.11, + } + } + }) + ); + } + } +} diff --git a/crates/ruma-client-api/src/r0/thirdparty.rs b/crates/ruma-client-api/src/thirdparty.rs similarity index 100% rename from crates/ruma-client-api/src/r0/thirdparty.rs rename to crates/ruma-client-api/src/thirdparty.rs diff --git a/crates/ruma-client-api/src/thirdparty/get_location_for_protocol.rs b/crates/ruma-client-api/src/thirdparty/get_location_for_protocol.rs new file mode 100644 index 00000000..31ebf632 --- /dev/null +++ b/crates/ruma-client-api/src/thirdparty/get_location_for_protocol.rs @@ -0,0 +1,58 @@ +//! `GET /_matrix/client/*/thirdparty/location/{protocol}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3thirdpartylocationprotocol + + use std::collections::BTreeMap; + + use ruma_api::ruma_api; + use ruma_common::thirdparty::Location; + + ruma_api! { + metadata: { + description: "Fetches third party locations for a protocol.", + method: GET, + name: "get_location_for_protocol", + r0_path: "/_matrix/client/r0/thirdparty/location/:protocol", + stable_path: "/_matrix/client/v3/thirdparty/location/:protocol", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The protocol used to communicate to the third party network. + #[ruma_api(path)] + pub protocol: &'a str, + + /// One or more custom fields to help identify the third party location. + // The specification is incorrect for this parameter. See matrix-org/matrix-doc#2352. + #[ruma_api(query_map)] + pub fields: BTreeMap, + } + + response: { + /// List of matched third party locations. + #[ruma_api(body)] + pub locations: Vec, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given protocol. + pub fn new(protocol: &'a str) -> Self { + Self { protocol, fields: BTreeMap::new() } + } + } + + impl Response { + /// Creates a new `Response` with the given locations. + pub fn new(locations: Vec) -> Self { + Self { locations } + } + } +} diff --git a/crates/ruma-client-api/src/thirdparty/get_location_for_room_alias.rs b/crates/ruma-client-api/src/thirdparty/get_location_for_room_alias.rs new file mode 100644 index 00000000..c0deef80 --- /dev/null +++ b/crates/ruma-client-api/src/thirdparty/get_location_for_room_alias.rs @@ -0,0 +1,52 @@ +//! `GET /_matrix/client/*/thirdparty/location` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3thirdpartylocation + + use ruma_api::ruma_api; + use ruma_common::thirdparty::Location; + use ruma_identifiers::RoomAliasId; + + ruma_api! { + metadata: { + description: "Retrieve an array of third party network locations from a Matrix room alias.", + method: GET, + name: "get_location_for_room_alias", + r0_path: "/_matrix/client/r0/thirdparty/location", + stable_path: "/_matrix/client/v3/thirdparty/location", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The Matrix room alias to look up. + #[ruma_api(query)] + pub alias: &'a RoomAliasId, + } + + response: { + /// List of matched third party locations. + #[ruma_api(body)] + pub locations: Vec, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given room alias ID. + pub fn new(alias: &'a RoomAliasId) -> Self { + Self { alias } + } + } + + impl Response { + /// Creates a new `Response` with the given locations. + pub fn new(locations: Vec) -> Self { + Self { locations } + } + } +} diff --git a/crates/ruma-client-api/src/thirdparty/get_protocol.rs b/crates/ruma-client-api/src/thirdparty/get_protocol.rs new file mode 100644 index 00000000..1438a2e8 --- /dev/null +++ b/crates/ruma-client-api/src/thirdparty/get_protocol.rs @@ -0,0 +1,51 @@ +//! `GET /_matrix/client/*/thirdparty/protocol/{protocol}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3thirdpartyprotocolprotocol + + use ruma_api::ruma_api; + use ruma_common::thirdparty::Protocol; + + ruma_api! { + metadata: { + description: "Fetches the metadata from the homeserver about a particular third party protocol.", + method: GET, + name: "get_protocol", + r0_path: "/_matrix/client/r0/thirdparty/protocol/:protocol", + stable_path: "/_matrix/client/v3/thirdparty/protocol/:protocol", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The name of the protocol. + #[ruma_api(path)] + pub protocol: &'a str, + } + + response: { + /// Metadata about the protocol. + #[ruma_api(body)] + pub protocol: Protocol, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given protocol name. + pub fn new(protocol: &'a str) -> Self { + Self { protocol } + } + } + + impl Response { + /// Creates a new `Response` with the given protocol. + pub fn new(protocol: Protocol) -> Self { + Self { protocol } + } + } +} diff --git a/crates/ruma-client-api/src/thirdparty/get_protocols.rs b/crates/ruma-client-api/src/thirdparty/get_protocols.rs new file mode 100644 index 00000000..eecced9b --- /dev/null +++ b/crates/ruma-client-api/src/thirdparty/get_protocols.rs @@ -0,0 +1,50 @@ +//! `GET /_matrix/client/*/thirdparty/protocols` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3thirdpartyprotocols + + use std::collections::BTreeMap; + + use ruma_api::ruma_api; + use ruma_common::thirdparty::Protocol; + + ruma_api! { + metadata: { + description: "Fetches the overall metadata about protocols supported by the homeserver.", + method: GET, + name: "get_protocols", + r0_path: "/_matrix/client/r0/thirdparty/protocols", + stable_path: "/_matrix/client/v3/thirdparty/protocols", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// Metadata about protocols supported by the homeserver. + #[ruma_api(body)] + pub protocols: BTreeMap, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given protocols. + pub fn new(protocols: BTreeMap) -> Self { + Self { protocols } + } + } +} diff --git a/crates/ruma-client-api/src/thirdparty/get_user_for_protocol.rs b/crates/ruma-client-api/src/thirdparty/get_user_for_protocol.rs new file mode 100644 index 00000000..d4ffc989 --- /dev/null +++ b/crates/ruma-client-api/src/thirdparty/get_user_for_protocol.rs @@ -0,0 +1,58 @@ +//! `GET /_matrix/client/*/thirdparty/user/{protocol}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3thirdpartyuserprotocol + + use std::collections::BTreeMap; + + use ruma_api::ruma_api; + use ruma_common::thirdparty::User; + + ruma_api! { + metadata: { + description: "Fetches third party users for a protocol.", + method: GET, + name: "get_user_for_protocol", + r0_path: "/_matrix/client/r0/thirdparty/user/:protocol", + stable_path: "/_matrix/client/v3/thirdparty/user/:protocol", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The protocol used to communicate to the third party network. + #[ruma_api(path)] + pub protocol: &'a str, + + /// One or more custom fields that are passed to the AS to help identify the user. + // The specification is incorrect for this parameter. See matrix-org/matrix-doc#2352. + #[ruma_api(query_map)] + pub fields: BTreeMap, + } + + response: { + /// List of matched third party users. + #[ruma_api(body)] + pub users: Vec, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given protocol. + pub fn new(protocol: &'a str) -> Self { + Self { protocol, fields: BTreeMap::new() } + } + } + + impl Response { + /// Creates a new `Response` with the given users. + pub fn new(users: Vec) -> Self { + Self { users } + } + } +} diff --git a/crates/ruma-client-api/src/thirdparty/get_user_for_user_id.rs b/crates/ruma-client-api/src/thirdparty/get_user_for_user_id.rs new file mode 100644 index 00000000..dd85b7da --- /dev/null +++ b/crates/ruma-client-api/src/thirdparty/get_user_for_user_id.rs @@ -0,0 +1,52 @@ +//! `GET /_matrix/client/*/thirdparty/user` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3thirdpartyuser + + use ruma_api::ruma_api; + use ruma_common::thirdparty::User; + use ruma_identifiers::UserId; + + ruma_api! { + metadata: { + description: "Retrieve an array of third party users from a Matrix User ID.", + method: GET, + name: "get_user_for_user_id", + r0_path: "/_matrix/client/r0/thirdparty/user", + stable_path: "/_matrix/client/v3/thirdparty/user", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The Matrix User ID to look up. + #[ruma_api(query)] + pub userid: &'a UserId, + } + + response: { + /// List of matched third party users. + #[ruma_api(body)] + pub users: Vec, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID. + pub fn new(userid: &'a UserId) -> Self { + Self { userid } + } + } + + impl Response { + /// Creates a new `Response` with the given users. + pub fn new(users: Vec) -> Self { + Self { users } + } + } +} diff --git a/crates/ruma-client-api/src/r0/to_device.rs b/crates/ruma-client-api/src/to_device.rs similarity index 100% rename from crates/ruma-client-api/src/r0/to_device.rs rename to crates/ruma-client-api/src/to_device.rs diff --git a/crates/ruma-client-api/src/to_device/send_event_to_device.rs b/crates/ruma-client-api/src/to_device/send_event_to_device.rs new file mode 100644 index 00000000..e4c53092 --- /dev/null +++ b/crates/ruma-client-api/src/to_device/send_event_to_device.rs @@ -0,0 +1,69 @@ +//! `PUT /_matrix/client/*/sendToDevice/{eventType}/{txnId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3sendtodeviceeventtypetxnid + + use std::collections::BTreeMap; + + use ruma_api::ruma_api; + use ruma_common::to_device::DeviceIdOrAllDevices; + use ruma_events::AnyToDeviceEventContent; + use ruma_identifiers::{TransactionId, UserId}; + use ruma_serde::Raw; + + ruma_api! { + metadata: { + description: "Send an event to a device or devices.", + method: PUT, + name: "send_event_to_device", + r0_path: "/_matrix/client/r0/sendToDevice/:event_type/:txn_id", + stable_path: "/_matrix/client/v3/sendToDevice/:event_type/:txn_id", + rate_limited: false, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// Type of event being sent to each device. + #[ruma_api(path)] + pub event_type: &'a str, + + /// A request identifier unique to the access token used to send the request. + #[ruma_api(path)] + pub txn_id: &'a TransactionId, + + /// Messages to send. + /// + /// Different message events can be sent to different devices in the same request, but all + /// events within one request must be of the same type. + pub messages: Messages, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given event type, transaction ID and raw messages. + pub fn new_raw(event_type: &'a str, txn_id: &'a TransactionId, messages: Messages) -> Self { + Self { event_type, txn_id, messages } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } + + /// Messages to send in a send-to-device request. + /// + /// Represented as a map of `{ user-ids => { device-ids => message-content } }`. + pub type Messages = + BTreeMap, BTreeMap>>; +} diff --git a/crates/ruma-client-api/src/r0/typing.rs b/crates/ruma-client-api/src/typing.rs similarity index 100% rename from crates/ruma-client-api/src/r0/typing.rs rename to crates/ruma-client-api/src/typing.rs diff --git a/crates/ruma-client-api/src/typing/create_typing_event.rs b/crates/ruma-client-api/src/typing/create_typing_event.rs new file mode 100644 index 00000000..8d084429 --- /dev/null +++ b/crates/ruma-client-api/src/typing/create_typing_event.rs @@ -0,0 +1,107 @@ +//! `PUT /_matrix/client/*/rooms/{roomId}/typing/{userId}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidtypinguserid + + use std::time::Duration; + + use ruma_api::ruma_api; + use ruma_identifiers::{RoomId, UserId}; + use serde::{de::Error, Deserialize, Deserializer, Serialize}; + + ruma_api! { + metadata: { + method: PUT, + r0_path: "/_matrix/client/r0/rooms/:room_id/typing/:user_id", + stable_path: "/_matrix/client/v3/rooms/:room_id/typing/:user_id", + name: "create_typing_event", + description: "Send a typing event to a room.", + authentication: AccessToken, + rate_limited: true, + added: 1.0, + } + + request: { + /// The user who has started to type. + #[ruma_api(path)] + pub user_id: &'a UserId, + + /// The room in which the user is typing. + #[ruma_api(path)] + pub room_id: &'a RoomId, + + /// Whether the user is typing within a length of time or not. + #[serde(flatten)] + pub state: Typing, + } + + #[derive(Default)] + response: {} + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given user ID, room ID and typing state. + pub fn new(user_id: &'a UserId, room_id: &'a RoomId, state: Typing) -> Self { + Self { user_id, room_id, state } + } + } + + impl Response { + /// Creates an empty `Response`. + pub fn new() -> Self { + Self {} + } + } + + /// A mark for whether the user is typing within a length of time or not. + #[derive(Clone, Copy, Debug, Serialize)] + #[serde(into = "TypingInner")] + #[allow(clippy::exhaustive_enums)] + pub enum Typing { + /// Not typing. + No, + + /// Typing during the specified length of time. + Yes(Duration), + } + + #[derive(Deserialize, Serialize)] + struct TypingInner { + typing: bool, + + #[serde( + with = "ruma_serde::duration::opt_ms", + default, + skip_serializing_if = "Option::is_none" + )] + timeout: Option, + } + + impl From for TypingInner { + fn from(typing: Typing) -> Self { + match typing { + Typing::No => Self { typing: false, timeout: None }, + Typing::Yes(time) => Self { typing: true, timeout: Some(time) }, + } + } + } + + impl<'de> Deserialize<'de> for Typing { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let inner = TypingInner::deserialize(deserializer)?; + + match (inner.typing, inner.timeout) { + (false, _) => Ok(Self::No), + (true, Some(time)) => Ok(Self::Yes(time)), + _ => Err(D::Error::missing_field("timeout")), + } + } + } +} diff --git a/crates/ruma-client-api/src/r0/uiaa.rs b/crates/ruma-client-api/src/uiaa.rs similarity index 100% rename from crates/ruma-client-api/src/r0/uiaa.rs rename to crates/ruma-client-api/src/uiaa.rs diff --git a/crates/ruma-client-api/src/uiaa/get_uiaa_fallback_page.rs b/crates/ruma-client-api/src/uiaa/get_uiaa_fallback_page.rs new file mode 100644 index 00000000..6a33248e --- /dev/null +++ b/crates/ruma-client-api/src/uiaa/get_uiaa_fallback_page.rs @@ -0,0 +1,64 @@ +//! `GET /_matrix/client/*/auth/{auth_type}/fallback/web?session={session_id}` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#fallback + + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "Get UIAA fallback web page.", + method: GET, + name: "authorize_fallback", + r0_path: "/_matrix/client/r0/auth/:auth_type/fallback/web", + stable_path: "/_matrix/client/v3/auth/:auth_type/fallback/web", + rate_limited: false, + authentication: None, + added: 1.0, + } + + request: { + /// The type name ("m.login.dummy", etc.) of the uiaa stage to get a fallback page for. + #[ruma_api(path)] + pub auth_type: String, + + /// The ID of the session given by the homeserver. + #[ruma_api(query)] + pub session: String, + } + + #[derive(Default)] + response: { + /// Optional URI to redirect to. + #[ruma_api(header = LOCATION)] + pub redirect_url: Option, + + /// HTML to return to client. + #[ruma_api(raw_body)] + pub body: Vec, + } + + error: crate::Error + } + + impl Request { + /// Creates a new `Request` with the given auth type and session ID. + pub fn new(auth_type: String, session: String) -> Self { + Self { auth_type, session } + } + } + + impl Response { + /// Creates a new `Response` with the given HTML body. + pub fn new(body: Vec) -> Self { + Self { redirect_url: None, body } + } + + /// Creates a new `Response` with the given redirect URL and an empty body. + pub fn redirect(url: String) -> Self { + Self { redirect_url: Some(url), body: Vec::new() } + } + } +} diff --git a/crates/ruma-client-api/src/r0/uiaa/user_serde.rs b/crates/ruma-client-api/src/uiaa/user_serde.rs similarity index 99% rename from crates/ruma-client-api/src/r0/uiaa/user_serde.rs rename to crates/ruma-client-api/src/uiaa/user_serde.rs index 7ae94736..5a21dcab 100644 --- a/crates/ruma-client-api/src/r0/uiaa/user_serde.rs +++ b/crates/ruma-client-api/src/uiaa/user_serde.rs @@ -20,9 +20,10 @@ pub(crate) enum UserIdentifier<'a> { impl<'a> From> for UserIdentifier<'a> { fn from(id: super::UserIdentifier<'a>) -> Self { - use super::UserIdentifier as SuperId; use UserIdentifier as SerdeId; + use super::UserIdentifier as SuperId; + match id { SuperId::MatrixId(user) => SerdeId::MatrixId { user }, SuperId::ThirdPartyId { address, medium } => SerdeId::ThirdPartyId { address, medium }, @@ -33,9 +34,10 @@ impl<'a> From> for UserIdentifier<'a> { impl From for super::IncomingUserIdentifier { fn from(id: IncomingUserIdentifier) -> super::IncomingUserIdentifier { - use super::IncomingUserIdentifier as SuperId; use IncomingUserIdentifier as SerdeId; + use super::IncomingUserIdentifier as SuperId; + match id { SerdeId::MatrixId { user } => SuperId::MatrixId(user), SerdeId::ThirdPartyId { address, medium } => SuperId::ThirdPartyId { address, medium }, diff --git a/crates/ruma-client-api/src/r0/user_directory.rs b/crates/ruma-client-api/src/user_directory.rs similarity index 100% rename from crates/ruma-client-api/src/r0/user_directory.rs rename to crates/ruma-client-api/src/user_directory.rs diff --git a/crates/ruma-client-api/src/user_directory/search_users.rs b/crates/ruma-client-api/src/user_directory/search_users.rs new file mode 100644 index 00000000..7f50db98 --- /dev/null +++ b/crates/ruma-client-api/src/user_directory/search_users.rs @@ -0,0 +1,106 @@ +//! `POST /_matrix/client/*/user_directory/search` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3user_directorysearch + + use js_int::{uint, UInt}; + use ruma_api::ruma_api; + use ruma_identifiers::{MxcUri, UserId}; + use serde::{Deserialize, Serialize}; + + ruma_api! { + metadata: { + description: "Performs a search for users.", + method: POST, + name: "search_users", + r0_path: "/_matrix/client/r0/user_directory/search", + stable_path: "/_matrix/client/v3/user_directory/search", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + request: { + /// The term to search for. + pub search_term: &'a str, + + /// The maximum number of results to return. + /// + /// Defaults to 10. + #[serde(default = "default_limit", skip_serializing_if = "is_default_limit")] + pub limit: UInt, + + /// Language tag to determine the collation to use for the (case-insensitive) search. + /// + /// See [MDN] for the syntax. + /// + /// [MDN]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language#Syntax + #[ruma_api(header = ACCEPT_LANGUAGE)] + pub language: Option, + } + + response: { + /// Ordered by rank and then whether or not profile info is available. + pub results: Vec, + + /// Indicates if the result list has been truncated by the limit. + pub limited: bool, + } + + error: crate::Error + } + + impl<'a> Request<'a> { + /// Creates a new `Request` with the given search term. + pub fn new(search_term: &'a str) -> Self { + Self { search_term, limit: default_limit(), language: None } + } + } + + impl Response { + /// Creates a new `Response` with the given results and limited flag + pub fn new(results: Vec, limited: bool) -> Self { + Self { results, limited } + } + } + + fn default_limit() -> UInt { + uint!(10) + } + + fn is_default_limit(limit: &UInt) -> bool { + limit == &default_limit() + } + + /// User data as result of a search. + #[derive(Clone, Debug, Deserialize, Serialize)] + #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] + pub struct User { + /// The user's matrix user ID. + pub user_id: Box, + + /// The display name of the user, if one exists. + #[serde(skip_serializing_if = "Option::is_none")] + pub display_name: Option, + + /// The avatar url, as an MXC, if one exists. + /// + /// If you activate the `compat` feature, this field being an empty string in JSON will + /// result in `None` here during deserialization. + #[serde(skip_serializing_if = "Option::is_none")] + #[cfg_attr( + feature = "compat", + serde(default, deserialize_with = "ruma_serde::empty_string_as_none") + )] + pub avatar_url: Option>, + } + + impl User { + /// Create a new `User` with the given `UserId`. + pub fn new(user_id: Box) -> Self { + Self { user_id, display_name: None, avatar_url: None } + } + } +} diff --git a/crates/ruma-client-api/src/r0/voip.rs b/crates/ruma-client-api/src/voip.rs similarity index 100% rename from crates/ruma-client-api/src/r0/voip.rs rename to crates/ruma-client-api/src/voip.rs diff --git a/crates/ruma-client-api/src/voip/get_turn_server_info.rs b/crates/ruma-client-api/src/voip/get_turn_server_info.rs new file mode 100644 index 00000000..8e3a85d6 --- /dev/null +++ b/crates/ruma-client-api/src/voip/get_turn_server_info.rs @@ -0,0 +1,58 @@ +//! `GET /_matrix/client/*/voip/turnServer` + +pub mod v3 { + //! `/v3/` ([spec]) + //! + //! [spec]: https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3voipturnserver + + use std::time::Duration; + + use ruma_api::ruma_api; + + ruma_api! { + metadata: { + description: "Get credentials for the client to use when initiating VoIP calls.", + method: GET, + name: "turn_server_info", + r0_path: "/_matrix/client/r0/voip/turnServer", + stable_path: "/_matrix/client/v3/voip/turnServer", + rate_limited: true, + authentication: AccessToken, + added: 1.0, + } + + #[derive(Default)] + request: {} + + response: { + /// The username to use. + pub username: String, + + /// The password to use. + pub password: String, + + /// A list of TURN URIs. + pub uris: Vec, + + /// The time-to-live in seconds. + #[serde(with = "ruma_serde::duration::secs")] + pub ttl: Duration, + } + + error: crate::Error + } + + impl Request { + /// Creates an empty `Request`. + pub fn new() -> Self { + Self {} + } + } + + impl Response { + /// Creates a new `Response` with the given username, password, TURN URIs and time-to-live. + pub fn new(username: String, password: String, uris: Vec, ttl: Duration) -> Self { + Self { username, password, uris, ttl } + } + } +} diff --git a/crates/ruma-client-api/tests/headers.rs b/crates/ruma-client-api/tests/headers.rs index 5fa39fa0..4704e3c8 100644 --- a/crates/ruma-client-api/tests/headers.rs +++ b/crates/ruma-client-api/tests/headers.rs @@ -2,7 +2,7 @@ use http::HeaderMap; use ruma_api::{MatrixVersion, OutgoingRequest as _, SendAccessToken}; -use ruma_client_api::unversioned::discover_homeserver; +use ruma_client_api::discover::discover_homeserver; #[test] fn get_request_headers() { diff --git a/crates/ruma-client-api/tests/uiaa.rs b/crates/ruma-client-api/tests/uiaa.rs index d660b7af..33f8061d 100644 --- a/crates/ruma-client-api/tests/uiaa.rs +++ b/crates/ruma-client-api/tests/uiaa.rs @@ -1,18 +1,17 @@ use assign::assign; use matches::assert_matches; use ruma_api::{EndpointError, OutgoingResponse}; -use serde_json::{ - from_slice as from_json_slice, from_str as from_json_str, from_value as from_json_value, json, - to_value as to_json_value, value::to_raw_value as to_raw_json_value, Value as JsonValue, -}; - use ruma_client_api::{ error::{ErrorBody, ErrorKind}, - r0::uiaa::{ + uiaa::{ self, AuthData, AuthFlow, AuthType, IncomingAuthData, IncomingUserIdentifier, UiaaInfo, UiaaResponse, }, }; +use serde_json::{ + from_slice as from_json_slice, from_str as from_json_str, from_value as from_json_value, json, + to_value as to_json_value, value::to_raw_value as to_raw_json_value, Value as JsonValue, +}; #[test] fn deserialize_user_identifier() { diff --git a/crates/ruma-client/src/client_api.rs b/crates/ruma-client/src/client_api.rs index 1aa982e4..585fc0cb 100644 --- a/crates/ruma-client/src/client_api.rs +++ b/crates/ruma-client/src/client_api.rs @@ -4,9 +4,9 @@ use assign::assign; use async_stream::try_stream; use futures_core::stream::Stream; use ruma_api::MatrixVersion; -use ruma_client_api::r0::{ +use ruma_client_api::{ account::register::{self, RegistrationKind}, - session::login::{self, LoginInfo}, + session::login::{self, v3::LoginInfo}, sync::sync_events, uiaa::UserIdentifier, }; @@ -27,10 +27,10 @@ impl Client { password: &str, device_id: Option<&DeviceId>, initial_device_display_name: Option<&str>, - ) -> Result> { + ) -> Result> { let response = self - .send_request(assign!(login::Request::new( - LoginInfo::Password(login::Password::new(UserIdentifier::MatrixId(user), password))), { + .send_request(assign!(login::v3::Request::new( + LoginInfo::Password(login::v3::Password::new(UserIdentifier::MatrixId(user), password))), { device_id, initial_device_display_name, } @@ -48,10 +48,10 @@ impl Client { /// returned by the endpoint in this client, in addition to returning it. pub async fn register_guest( &self, - ) -> Result> { + ) -> Result> { let response = self .send_request( - assign!(register::Request::new(), { kind: RegistrationKind::Guest }), + assign!(register::v3::Request::new(), { kind: RegistrationKind::Guest }), &[MatrixVersion::V1_0], ) .await?; @@ -72,10 +72,10 @@ impl Client { &self, username: Option<&str>, password: &str, - ) -> Result> { + ) -> Result> { let response = self .send_request( - assign!(register::Request::new(), { username, password: Some(password)}), + assign!(register::v3::Request::new(), { username, password: Some(password)}), &[MatrixVersion::V1_0], ) .await?; @@ -113,16 +113,16 @@ impl Client { /// ``` pub fn sync<'a>( &'a self, - filter: Option<&'a sync_events::Filter<'a>>, + filter: Option<&'a sync_events::v3::Filter<'a>>, mut since: String, set_presence: &'a PresenceState, timeout: Option, - ) -> impl Stream>> + 'a - { + ) -> impl Stream>> + + 'a { try_stream! { loop { let response = self - .send_request(assign!(sync_events::Request::new(), { + .send_request(assign!(sync_events::v3::Request::new(), { filter, since: Some(&since), set_presence, diff --git a/crates/ruma-client/src/lib.rs b/crates/ruma-client/src/lib.rs index d58b7227..419ac815 100644 --- a/crates/ruma-client/src/lib.rs +++ b/crates/ruma-client/src/lib.rs @@ -56,13 +56,13 @@ //! # let client = MatrixClient::new(homeserver_url, None); //! use std::convert::TryFrom; //! -//! use ruma_client_api::r0::alias::get_alias; +//! use ruma_client_api::alias::get_alias; //! use ruma_identifiers::{room_alias_id, room_id}; //! use ruma_api::MatrixVersion; //! //! async { //! let response = client -//! .send_request(get_alias::Request::new(room_alias_id!("#example_room:example.com")), &[MatrixVersion::V1_0]) +//! .send_request(get_alias::v3::Request::new(room_alias_id!("#example_room:example.com")), &[MatrixVersion::V1_0]) //! .await?; //! //! assert_eq!(response.room_id, room_id!("!n8f893n9:example.com")); diff --git a/crates/ruma/examples/hello_isahc.rs b/crates/ruma/examples/hello_isahc.rs index 16e55db0..5db24823 100644 --- a/crates/ruma/examples/hello_isahc.rs +++ b/crates/ruma/examples/hello_isahc.rs @@ -4,10 +4,11 @@ use std::{convert::TryFrom, env, process::exit}; use ruma::{ - api::client::r0::{alias::get_alias, membership::join_room_by_id, message::send_message_event}, + api::client::{alias::get_alias, membership::join_room_by_id, message::send_message_event}, events::room::message::RoomMessageEventContent, RoomAliasId, }; +use ruma_api::MatrixVersion; type MatrixClient = ruma_client::Client; @@ -21,14 +22,22 @@ async fn hello_world( let client = MatrixClient::with_http_client(http_client, homeserver_url, None); client.log_in(username, password, None, Some("ruma-example-client")).await?; - let room_id = client.send_request(get_alias::Request::new(room_alias)).await?.room_id; - client.send_request(join_room_by_id::Request::new(&room_id)).await?; + let room_id = client + .send_request(get_alias::v3::Request::new(room_alias), &[MatrixVersion::V1_0]) + .await? + .room_id; client - .send_request(send_message_event::Request::new( - &room_id, - "1", - &RoomMessageEventContent::text_plain("Hello World!"), - )?) + .send_request(join_room_by_id::v3::Request::new(&room_id), &[MatrixVersion::V1_0]) + .await?; + client + .send_request( + send_message_event::Request::new( + &room_id, + "1", + &RoomMessageEventContent::text_plain("Hello World!"), + )?, + &[MatrixVersion::V1_0], + ) .await?; Ok(()) diff --git a/crates/ruma/examples/hello_world.rs b/crates/ruma/examples/hello_world.rs index 2affd3d3..f96b0b3b 100644 --- a/crates/ruma/examples/hello_world.rs +++ b/crates/ruma/examples/hello_world.rs @@ -1,7 +1,7 @@ use std::{convert::TryFrom, env, process::exit}; use ruma::{ - api::client::r0::{alias::get_alias, membership::join_room_by_id, message::send_message_event}, + api::client::{alias::get_alias, membership::join_room_by_id, message::send_message_event}, events::room::message::RoomMessageEventContent, RoomAliasId, }; @@ -20,13 +20,15 @@ async fn hello_world( client.log_in(username, password, None, Some("ruma-example-client")).await?; let room_id = client - .send_request(get_alias::Request::new(room_alias), &[MatrixVersion::V1_0]) + .send_request(get_alias::v3::Request::new(room_alias), &[MatrixVersion::V1_0]) .await? .room_id; - client.send_request(join_room_by_id::Request::new(&room_id), &[MatrixVersion::V1_0]).await?; + client + .send_request(join_room_by_id::v3::Request::new(&room_id), &[MatrixVersion::V1_0]) + .await?; client .send_request( - send_message_event::Request::new( + send_message_event::v3::Request::new( &room_id, &TransactionId::new(), &RoomMessageEventContent::text_plain("Hello World!"), diff --git a/crates/ruma/examples/message_log.rs b/crates/ruma/examples/message_log.rs index 1d122be3..cad44909 100644 --- a/crates/ruma/examples/message_log.rs +++ b/crates/ruma/examples/message_log.rs @@ -2,7 +2,7 @@ use std::{env, process::exit, time::Duration}; use assign::assign; use ruma::{ - api::client::r0::{filter::FilterDefinition, sync::sync_events}, + api::client::{filter::FilterDefinition, sync::sync_events}, events::{ room::message::{MessageType, RoomMessageEventContent, TextMessageEventContent}, AnySyncMessageEvent, AnySyncRoomEvent, SyncMessageEvent, @@ -25,7 +25,7 @@ async fn log_messages( let filter = FilterDefinition::ignore_all().into(); let initial_sync_response = client .send_request( - assign!(sync_events::Request::new(), { + assign!(sync_events::v3::Request::new(), { filter: Some(&filter), }), &[ruma_api::MatrixVersion::V1_0], diff --git a/examples/joke_bot/src/main.rs b/examples/joke_bot/src/main.rs index b1d94b0a..d438086e 100644 --- a/examples/joke_bot/src/main.rs +++ b/examples/joke_bot/src/main.rs @@ -3,7 +3,7 @@ use std::{convert::TryInto, error::Error, io, process::exit, time::Duration}; use futures_util::future::{join, join_all}; use ruma::{ api::{ - client::r0::{ + client::{ filter::FilterDefinition, membership::join_room_by_id, message::send_message_event, sync::sync_events, }, @@ -64,7 +64,7 @@ async fn run() -> Result<(), Box> { let filter = FilterDefinition::ignore_all().into(); let initial_sync_response = matrix_client .send_request( - assign!(sync_events::Request::new(), { + assign!(sync_events::v3::Request::new(), { filter: Some(&filter), }), &[MatrixVersion::V1_0], @@ -165,7 +165,7 @@ async fn handle_message( let joke_content = RoomMessageEventContent::text_plain(joke); let txn_id = TransactionId::new(); - let req = send_message_event::Request::new(room_id, &txn_id, &joke_content)?; + let req = send_message_event::v3::Request::new(room_id, &txn_id, &joke_content)?; // Do nothing if we can't send the message. let _ = matrix_client.send_request(req, &[MatrixVersion::V1_0]).await; } @@ -181,14 +181,14 @@ async fn handle_invitations( ) -> Result<(), Box> { println!("invited to {}", &room_id); matrix_client - .send_request(join_room_by_id::Request::new(room_id), &[MatrixVersion::V1_0]) + .send_request(join_room_by_id::v3::Request::new(room_id), &[MatrixVersion::V1_0]) .await?; let greeting = "Hello! My name is Mr. Bot! I like to tell jokes. Like this one: "; let joke = get_joke(http_client).await.unwrap_or_else(|_| "err... never mind.".to_owned()); let content = RoomMessageEventContent::text_plain(format!("{}\n{}", greeting, joke)); let txn_id = TransactionId::new(); - let message = send_message_event::Request::new(room_id, &txn_id, &content)?; + let message = send_message_event::v3::Request::new(room_id, &txn_id, &content)?; matrix_client.send_request(message, &[MatrixVersion::V1_0]).await?; Ok(()) }