From b24df92692abd8cb45cad74fc791a8a0b9298c1c Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 22 Jul 2020 18:57:43 +0200 Subject: [PATCH] Make DeviceId a newtype rather than an alias --- ruma-events/src/key/verification/start.rs | 4 +- ruma-events/src/room/encrypted.rs | 2 +- ruma-identifiers/CHANGELOG.md | 6 +- ruma-identifiers/src/device_id.rs | 134 +++++++++++++++++++++- ruma-identifiers/src/device_key_id.rs | 2 +- 5 files changed, 137 insertions(+), 11 deletions(-) diff --git a/ruma-events/src/key/verification/start.rs b/ruma-events/src/key/verification/start.rs index 46551377..2388ae40 100644 --- a/ruma-events/src/key/verification/start.rs +++ b/ruma-events/src/key/verification/start.rs @@ -288,7 +288,7 @@ mod tests { key_agreement_protocols, message_authentication_codes, short_authentication_string, - }) if from_device.as_ref() == "123" + }) if from_device.as_str() == "123" && transaction_id == "456" && hashes == vec![HashAlgorithm::Sha256] && key_agreement_protocols == vec![KeyAgreementProtocol::Curve25519] @@ -323,7 +323,7 @@ mod tests { message_authentication_codes, short_authentication_string, }) - } if from_device.as_ref() == "123" + } if from_device.as_str() == "123" && transaction_id == "456" && hashes == vec![HashAlgorithm::Sha256] && key_agreement_protocols == vec![KeyAgreementProtocol::Curve25519] diff --git a/ruma-events/src/room/encrypted.rs b/ruma-events/src/room/encrypted.rs index 801ce71a..2e986c66 100644 --- a/ruma-events/src/room/encrypted.rs +++ b/ruma-events/src/room/encrypted.rs @@ -161,7 +161,7 @@ mod tests { session_id, }) if ciphertext == "ciphertext" && sender_key == "sender_key" - && device_id.as_ref() == "device_id" + && device_id.as_str() == "device_id" && session_id == "session_id" ); } diff --git a/ruma-identifiers/CHANGELOG.md b/ruma-identifiers/CHANGELOG.md index a095c02f..1506ceb3 100644 --- a/ruma-identifiers/CHANGELOG.md +++ b/ruma-identifiers/CHANGELOG.md @@ -32,9 +32,9 @@ Breaking changes: ```rust fn server_name() -> &ServerName ``` -* Change `DeviceId` from being an alias for `String` to being an alias for `str` - * This means any string slice or string literal is a valid `&DeviceId` now - * But to store one, you need to box it: `Box` +* Change `DeviceId` from being an alias for `String` to being a newtype around `str` + + This means owned device IDs are now `Box`. * Change `RoomVersionId` from being an opaque struct to a non-exhaustive enum * The constructor functions and `is_` predicates are now deprecated diff --git a/ruma-identifiers/src/device_id.rs b/ruma-identifiers/src/device_id.rs index e6993390..32a570d9 100644 --- a/ruma-identifiers/src/device_id.rs +++ b/ruma-identifiers/src/device_id.rs @@ -1,5 +1,10 @@ //! Matrix device identifiers. +use std::{ + fmt::{self, Display}, + mem, +}; + #[cfg(feature = "rand")] use crate::generate_localpart; @@ -7,21 +12,142 @@ use crate::generate_localpart; /// /// Device identifiers in Matrix are completely opaque character sequences. This type alias is /// provided simply for its semantic value. -pub type DeviceId = str; +#[repr(transparent)] +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(serde::Serialize), serde(transparent))] +pub struct DeviceId(str); + +impl DeviceId { + #[allow(clippy::transmute_ptr_to_ptr)] + fn from_borrowed(s: &str) -> &Self { + unsafe { mem::transmute(s) } + } + + fn from_owned(s: Box) -> Box { + unsafe { mem::transmute(s) } + } + + fn into_owned(self: Box) -> Box { + unsafe { mem::transmute(self) } + } + + /// Generates a random `DeviceId`, suitable for assignment to a new device. + #[cfg(feature = "rand")] + #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] + pub fn new() -> Box { + Self::from_owned(generate_localpart(8)) + } + + /// Creates a string slice from this `DeviceId`. + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl Clone for Box { + fn clone(&self) -> Self { + (**self).to_owned() + } +} + +impl ToOwned for DeviceId { + type Owned = Box; + + fn to_owned(&self) -> Self::Owned { + Self::from_owned(self.0.to_owned().into_boxed_str()) + } +} + +impl AsRef for DeviceId { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl AsRef for Box { + fn as_ref(&self) -> &str { + self.as_str() + } +} + +impl<'a> From<&'a str> for &'a DeviceId { + fn from(s: &'a str) -> Self { + DeviceId::from_borrowed(s) + } +} + +impl From<&str> for Box { + fn from(s: &str) -> Self { + DeviceId::from_owned(s.into()) + } +} + +impl From for Box { + fn from(s: String) -> Self { + DeviceId::from_owned(s.into()) + } +} + +impl From> for String { + fn from(id: Box) -> Self { + id.into_owned().into() + } +} + +impl Display for DeviceId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.as_str()) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for Box { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + crate::deserialize_id(deserializer, "An IP address or hostname") + } +} + +impl PartialEq for DeviceId { + fn eq(&self, other: &str) -> bool { + self.as_str() == other + } +} + +impl PartialEq for str { + fn eq(&self, other: &DeviceId) -> bool { + self == other.as_str() + } +} + +impl PartialEq for DeviceId { + fn eq(&self, other: &String) -> bool { + self.as_str() == other.as_str() + } +} + +impl PartialEq for String { + fn eq(&self, other: &DeviceId) -> bool { + self.as_str() == other.as_str() + } +} /// Generates a random `DeviceId`, suitable for assignment to a new device. #[cfg(feature = "rand")] #[cfg_attr(docsrs, doc(cfg(feature = "rand")))] +#[deprecated = "use DeviceId::new instead"] pub fn generate() -> Box { - generate_localpart(8) + DeviceId::new() } #[cfg(all(test, feature = "rand"))] mod tests { - use super::generate; + use super::DeviceId; #[test] fn generate_device_id() { - assert_eq!(generate().len(), 8); + assert_eq!(DeviceId::new().as_str().len(), 8); } } diff --git a/ruma-identifiers/src/device_key_id.rs b/ruma-identifiers/src/device_key_id.rs index 2584b3ae..284bb4e5 100644 --- a/ruma-identifiers/src/device_key_id.rs +++ b/ruma-identifiers/src/device_key_id.rs @@ -18,7 +18,7 @@ impl DeviceKeyId { /// Returns device ID of the device key ID. pub fn device_id(&self) -> &DeviceId { - &self.full_id[self.colon_idx.get() as usize + 1..] + (&self.full_id[self.colon_idx.get() as usize + 1..]).into() } }