Make DeviceId a newtype rather than an alias

This commit is contained in:
Jonas Platte 2020-07-22 18:57:43 +02:00
parent 8d8e18afbc
commit b24df92692
No known key found for this signature in database
GPG Key ID: 7D261D771D915378
5 changed files with 137 additions and 11 deletions

View File

@ -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]

View File

@ -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"
);
}

View File

@ -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<DeviceId>`
* Change `DeviceId` from being an alias for `String` to being a newtype around `str`
This means owned device IDs are now `Box<DeviceId>`.
* Change `RoomVersionId` from being an opaque struct to a non-exhaustive enum
* The constructor functions and `is_` predicates are now deprecated

View File

@ -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<str>) -> Box<Self> {
unsafe { mem::transmute(s) }
}
fn into_owned(self: Box<Self>) -> Box<str> {
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> {
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<DeviceId> {
fn clone(&self) -> Self {
(**self).to_owned()
}
}
impl ToOwned for DeviceId {
type Owned = Box<DeviceId>;
fn to_owned(&self) -> Self::Owned {
Self::from_owned(self.0.to_owned().into_boxed_str())
}
}
impl AsRef<str> for DeviceId {
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl AsRef<str> for Box<DeviceId> {
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<DeviceId> {
fn from(s: &str) -> Self {
DeviceId::from_owned(s.into())
}
}
impl From<String> for Box<DeviceId> {
fn from(s: String) -> Self {
DeviceId::from_owned(s.into())
}
}
impl From<Box<DeviceId>> for String {
fn from(id: Box<DeviceId>) -> 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<DeviceId> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
crate::deserialize_id(deserializer, "An IP address or hostname")
}
}
impl PartialEq<str> for DeviceId {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<DeviceId> for str {
fn eq(&self, other: &DeviceId) -> bool {
self == other.as_str()
}
}
impl PartialEq<String> for DeviceId {
fn eq(&self, other: &String) -> bool {
self.as_str() == other.as_str()
}
}
impl PartialEq<DeviceId> 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<DeviceId> {
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);
}
}

View File

@ -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()
}
}