diff --git a/crates/ruma-client-api/src/error.rs b/crates/ruma-client-api/src/error.rs index a41ca9e2..9c8bc6ac 100644 --- a/crates/ruma-client-api/src/error.rs +++ b/crates/ruma-client-api/src/error.rs @@ -181,6 +181,7 @@ impl fmt::Display for ErrorKind { /// A Matrix Error without a status code #[derive(Debug, Clone, Serialize, Deserialize)] +#[allow(clippy::exhaustive_structs)] #[cfg_attr(test, derive(PartialEq))] pub struct ErrorBody { /// A value which can be used to handle an error message @@ -194,6 +195,7 @@ pub struct ErrorBody { /// A Matrix Error #[derive(Debug, Clone)] +#[allow(clippy::exhaustive_structs)] pub struct Error { /// A value which can be used to handle an error message pub kind: ErrorKind, diff --git a/crates/ruma-client-api/src/r0/backup.rs b/crates/ruma-client-api/src/r0/backup.rs index 052a39eb..373f2330 100644 --- a/crates/ruma-client-api/src/r0/backup.rs +++ b/crates/ruma-client-api/src/r0/backup.rs @@ -54,7 +54,11 @@ pub enum BackupAlgorithm { } /// Information about the backup key. +/// +/// To create an instance of this type, first create a `KeyBackupDataInit` and convert it via +/// `KeyBackupData::from` / `.into()`. #[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] pub struct KeyBackupData { /// The index of the first message in the session that the key can decrypt. pub first_message_index: UInt, @@ -69,8 +73,40 @@ pub struct KeyBackupData { pub session_data: SessionData, } -/// The algorithm used for storing backups. +/// Information about the backup key. +/// +/// This struct will not be updated even if additional fields are added to `SessionData` in a +/// new (non-breaking) release of the Matrix specification. #[derive(Clone, Debug, Serialize, Deserialize)] +#[allow(clippy::exhaustive_structs)] +pub struct KeyBackupDataInit { + /// The index of the first message in the session that the key can decrypt. + pub first_message_index: UInt, + + /// The number of times this key has been forwarded via key-sharing between devices. + pub forwarded_count: UInt, + + /// Whether the device backing up the key verified the device that the key is from. + pub is_verified: bool, + + /// Data about the session. + pub session_data: SessionData, +} + +impl From for KeyBackupData { + fn from(init: KeyBackupDataInit) -> Self { + let KeyBackupDataInit { first_message_index, forwarded_count, is_verified, session_data } = + init; + Self { first_message_index, forwarded_count, is_verified, session_data } + } +} + +/// The algorithm used for storing backups. +/// +/// To create an instance of this type, first create a `SessionDataInit` and convert it via +/// `SessionData::from` / `.into()`. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] pub struct SessionData { /// Unpadded base64-encoded public half of the ephemeral key. pub ephemeral: String, @@ -81,3 +117,27 @@ pub struct SessionData { /// First 8 bytes of MAC key, encoded in base64. pub mac: String, } + +/// The algorithm used for storing backups. +/// +/// This struct will not be updated even if additional fields are added to `SessionData` in a +/// new (non-breaking) release of the Matrix specification. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[allow(clippy::exhaustive_structs)] +pub struct SessionDataInit { + /// Unpadded base64-encoded public half of the ephemeral key. + pub ephemeral: String, + + /// Ciphertext, encrypted using AES-CBC-256 with PKCS#7 padding, encoded in base64. + pub ciphertext: String, + + /// First 8 bytes of MAC key, encoded in base64. + pub mac: String, +} + +impl From for SessionData { + fn from(init: SessionDataInit) -> Self { + let SessionDataInit { ephemeral, ciphertext, mac } = init; + Self { ephemeral, ciphertext, mac } + } +} diff --git a/crates/ruma-client-api/src/r0/membership.rs b/crates/ruma-client-api/src/r0/membership.rs index f399387d..2f7f3a8f 100644 --- a/crates/ruma-client-api/src/r0/membership.rs +++ b/crates/ruma-client-api/src/r0/membership.rs @@ -76,6 +76,7 @@ pub struct Invite3pid<'a> { /// This struct will not be updated even if additional fields are added to `Invite3pid` in a new /// (non-breaking) release of the Matrix specification. #[derive(Debug)] +#[allow(clippy::exhaustive_structs)] pub struct Invite3pidInit<'a> { /// Hostname and port of identity server to be used for account lookups. pub id_server: &'a str, diff --git a/crates/ruma-client-api/src/r0/push/get_pushers.rs b/crates/ruma-client-api/src/r0/push/get_pushers.rs index 927ea305..a8bc2572 100644 --- a/crates/ruma-client-api/src/r0/push/get_pushers.rs +++ b/crates/ruma-client-api/src/r0/push/get_pushers.rs @@ -41,7 +41,11 @@ impl Response { } /// 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 { /// This is a unique identifier for this pusher. Max length, 512 bytes. pub pushkey: String, @@ -68,3 +72,60 @@ pub struct Pusher { /// 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 { + /// This is a unique identifier for this pusher. Max length, 512 bytes. + pub pushkey: String, + + /// The kind of the pusher. `None` deletes the pusher. + pub kind: PusherKind, + + /// This is a reverse-DNS style identifier for the application. Max length, 64 chars. + 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, + + /// This 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_pusher.rs b/crates/ruma-client-api/src/r0/push/set_pusher.rs index 3b80ffa1..db51ee80 100644 --- a/crates/ruma-client-api/src/r0/push/set_pusher.rs +++ b/crates/ruma-client-api/src/r0/push/set_pusher.rs @@ -48,7 +48,11 @@ impl Response { } /// 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 { /// This is a unique identifier for this pusher. Max length, 512 bytes. pub pushkey: String, @@ -75,3 +79,60 @@ pub struct Pusher { /// 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 { + /// This is a unique identifier for this pusher. Max length, 512 bytes. + pub pushkey: String, + + /// The kind of the pusher. `None` deletes the pusher. + pub kind: Option, + + /// This is a reverse-DNS style identifier for the application. Max length, 64 chars. + 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, + + /// This 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/server/get_user_info.rs b/crates/ruma-client-api/src/r0/server/get_user_info.rs index a4e181d0..23fc547e 100644 --- a/crates/ruma-client-api/src/r0/server/get_user_info.rs +++ b/crates/ruma-client-api/src/r0/server/get_user_info.rs @@ -52,23 +52,40 @@ impl Response { } /// Information about a user's device. -#[derive(Clone, Debug, Deserialize, Serialize)] +#[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, Deserialize, Serialize)] +#[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, Deserialize, Serialize)] +#[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, @@ -79,3 +96,10 @@ pub struct ConnectionInfo { /// 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/get_login_types.rs b/crates/ruma-client-api/src/r0/session/get_login_types.rs index 1a9e3b14..988d9de4 100644 --- a/crates/ruma-client-api/src/r0/session/get_login_types.rs +++ b/crates/ruma-client-api/src/r0/session/get_login_types.rs @@ -240,6 +240,7 @@ pub enum IdentityProviderBrand { /// A custom login payload. #[doc(hidden)] #[derive(Clone, Debug, Deserialize, Serialize)] +#[allow(clippy::exhaustive_structs)] pub struct CustomLoginType { /// A custom type /// diff --git a/crates/ruma-client-api/src/r0/session/login.rs b/crates/ruma-client-api/src/r0/session/login.rs index cfefa37e..c40c13e6 100644 --- a/crates/ruma-client-api/src/r0/session/login.rs +++ b/crates/ruma-client-api/src/r0/session/login.rs @@ -126,6 +126,7 @@ pub enum LoginInfo<'a> { /// 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")] @@ -136,20 +137,43 @@ pub struct DiscoveryInfo { 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 } + } +} + mod user_serde; #[cfg(test)] diff --git a/crates/ruma-client-api/src/r0/sync/sync_events.rs b/crates/ruma-client-api/src/r0/sync/sync_events.rs index 4228154b..afe1ad7d 100644 --- a/crates/ruma-client-api/src/r0/sync/sync_events.rs +++ b/crates/ruma-client-api/src/r0/sync/sync_events.rs @@ -1,5 +1,9 @@ //! [GET /_matrix/client/r0/sync](https://matrix.org/docs/spec/client_server/r0.6.0#get-matrix-client-r0-sync) +// FIXME: once https://github.com/rust-lang/rust/issues/84332 is resolved +// the structs can just be non_exhaustive (remove __test_exhaustive) +#![allow(clippy::exhaustive_structs)] + use std::{collections::BTreeMap, time::Duration}; use js_int::UInt; 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 index 09009010..79210bcd 100644 --- a/crates/ruma-client-api/src/r0/user_directory/search_users.rs +++ b/crates/ruma-client-api/src/r0/user_directory/search_users.rs @@ -69,6 +69,7 @@ fn is_default_limit(limit: &UInt) -> bool { /// 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: UserId, @@ -88,3 +89,10 @@ pub struct User { )] pub avatar_url: Option, } + +impl User { + /// Create a new `User` with the given `UserId`. + pub fn new(user_id: UserId) -> Self { + Self { user_id, display_name: None, avatar_url: None } + } +}