From 313124a099378ea76557bd32dab69353905111bb Mon Sep 17 00:00:00 2001 From: Jonas Platte Date: Wed, 24 Nov 2021 21:36:15 +0100 Subject: [PATCH] identifiers: Make RoomIdOrAliasId a DST --- .../src/r0/search/search_events.rs | 2 +- .../src/room_id_or_alias_id.rs | 8 +- .../src/room_id_or_room_alias_id.rs | 90 ++++++++----------- crates/ruma-serde-macros/src/outgoing.rs | 1 + 4 files changed, 43 insertions(+), 58 deletions(-) diff --git a/crates/ruma-client-api/src/r0/search/search_events.rs b/crates/ruma-client-api/src/r0/search/search_events.rs index ca9e0567..4eb66bf5 100644 --- a/crates/ruma-client-api/src/r0/search/search_events.rs +++ b/crates/ruma-client-api/src/r0/search/search_events.rs @@ -356,7 +356,7 @@ pub struct ResultRoomEvents { /// Any groups that were requested. #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub groups: BTreeMap>, + 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. diff --git a/crates/ruma-identifiers-validation/src/room_id_or_alias_id.rs b/crates/ruma-identifiers-validation/src/room_id_or_alias_id.rs index e477d164..e74716cb 100644 --- a/crates/ruma-identifiers-validation/src/room_id_or_alias_id.rs +++ b/crates/ruma-identifiers-validation/src/room_id_or_alias_id.rs @@ -1,7 +1,5 @@ -use std::num::NonZeroU8; +use crate::{validate_delimited_id, Error}; -use crate::{parse_id, Error}; - -pub fn validate(s: &str) -> Result { - parse_id(s, &['#', '!']) +pub fn validate(s: &str) -> Result<(), Error> { + validate_delimited_id(s, &['#', '!']) } diff --git a/crates/ruma-identifiers/src/room_id_or_room_alias_id.rs b/crates/ruma-identifiers/src/room_id_or_room_alias_id.rs index 7d70fa32..43a3e201 100644 --- a/crates/ruma-identifiers/src/room_id_or_room_alias_id.rs +++ b/crates/ruma-identifiers/src/room_id_or_room_alias_id.rs @@ -2,9 +2,7 @@ use std::{ convert::{TryFrom, TryInto}, - fmt, hint::unreachable_unchecked, - num::NonZeroU8, }; use crate::{server_name::ServerName, RoomAliasId, RoomId}; @@ -19,36 +17,32 @@ use crate::{server_name::ServerName, RoomAliasId, RoomId}; /// # use std::convert::TryFrom; /// # use ruma_identifiers::RoomIdOrAliasId; /// assert_eq!( -/// RoomIdOrAliasId::try_from("#ruma:example.com").unwrap().as_ref(), +/// <&RoomIdOrAliasId>::try_from("#ruma:example.com").unwrap(), /// "#ruma:example.com" /// ); /// /// assert_eq!( -/// RoomIdOrAliasId::try_from("!n8f893n9:example.com").unwrap().as_ref(), +/// <&RoomIdOrAliasId>::try_from("!n8f893n9:example.com").unwrap(), /// "!n8f893n9:example.com" /// ); /// ``` -#[derive(Clone)] -pub struct RoomIdOrAliasId { - full_id: Box, - colon_idx: NonZeroU8, -} +#[repr(transparent)] +pub struct RoomIdOrAliasId(str); -impl fmt::Debug for RoomIdOrAliasId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.full_id.fmt(f) - } -} +opaque_identifier_validated!( + RoomIdOrAliasId, + ruma_identifiers_validation::room_id_or_alias_id::validate +); impl RoomIdOrAliasId { /// Returns the local part (everything after the `!` or `#` and before the first colon). pub fn localpart(&self) -> &str { - &self.full_id[1..self.colon_idx.get() as usize] + &self.as_str()[1..self.colon_idx()] } /// Returns the server name of the room (alias) ID. pub fn server_name(&self) -> &ServerName { - self.full_id[self.colon_idx.get() as usize + 1..].try_into().unwrap() + self.as_str()[self.colon_idx() + 1..].try_into().unwrap() } /// Whether this is a room id (starts with `'!'`) @@ -63,15 +57,22 @@ impl RoomIdOrAliasId { /// Turn this `RoomIdOrAliasId` into `Either` #[cfg(feature = "either")] - pub fn into_either(self) -> either::Either, Box> { - match self.variant() { - Variant::RoomId => either::Either::Left(self.as_str().try_into().unwrap()), - Variant::RoomAliasId => either::Either::Right(self.as_str().try_into().unwrap()), + pub fn into_either(self: Box) -> either::Either, Box> { + let variant = self.variant(); + let boxed_str = self.into_owned(); + + match variant { + Variant::RoomId => either::Either::Left(RoomId::from_owned(boxed_str)), + Variant::RoomAliasId => either::Either::Right(RoomAliasId::from_owned(boxed_str)), } } + fn colon_idx(&self) -> usize { + self.as_str().find(':').unwrap() + } + fn variant(&self) -> Variant { - match self.full_id.bytes().next() { + match self.as_str().bytes().next() { Some(b'!') => Variant::RoomId, Some(b'#') => Variant::RoomAliasId, _ => unsafe { unreachable_unchecked() }, @@ -85,38 +86,22 @@ enum Variant { RoomAliasId, } -/// Attempts to create a new Matrix room ID or a room alias ID from a string representation. -/// -/// The string must either include the leading ! sigil, the localpart, a literal colon, and a -/// valid homeserver host or include the leading # sigil, the alias, a literal colon, and a -/// valid homeserver host. -fn try_from(room_id_or_alias_id: S) -> Result -where - S: AsRef + Into>, -{ - let colon_idx = - ruma_identifiers_validation::room_id_or_alias_id::validate(room_id_or_alias_id.as_ref())?; - Ok(RoomIdOrAliasId { full_id: room_id_or_alias_id.into(), colon_idx }) -} - -common_impls!(RoomIdOrAliasId, try_from, "a Matrix room ID or room alias ID"); - -impl From> for RoomIdOrAliasId { +impl From> for Box { fn from(room_id: Box) -> Self { Self::try_from(room_id.as_str()).unwrap() } } -impl From> for RoomIdOrAliasId { +impl From> for Box { fn from(room_alias_id: Box) -> Self { Self::try_from(room_alias_id.as_str()).unwrap() } } -impl TryFrom for Box { +impl TryFrom> for Box { type Error = Box; - fn try_from(id: RoomIdOrAliasId) -> Result, Box> { + fn try_from(id: Box) -> Result, Box> { match id.variant() { Variant::RoomId => Ok(id.as_str().try_into().unwrap()), Variant::RoomAliasId => Err(id.as_str().try_into().unwrap()), @@ -124,10 +109,10 @@ impl TryFrom for Box { } } -impl TryFrom for Box { +impl TryFrom> for Box { type Error = Box; - fn try_from(id: RoomIdOrAliasId) -> Result, Box> { + fn try_from(id: Box) -> Result, Box> { match id.variant() { Variant::RoomAliasId => Ok(id.as_str().try_into().unwrap()), Variant::RoomId => Err(id.as_str().try_into().unwrap()), @@ -145,7 +130,7 @@ mod tests { #[test] fn valid_room_id_or_alias_id_with_a_room_alias_id() { assert_eq!( - RoomIdOrAliasId::try_from("#ruma:example.com") + <&RoomIdOrAliasId>::try_from("#ruma:example.com") .expect("Failed to create RoomAliasId.") .as_ref(), "#ruma:example.com" @@ -155,7 +140,7 @@ mod tests { #[test] fn valid_room_id_or_alias_id_with_a_room_id() { assert_eq!( - RoomIdOrAliasId::try_from("!29fhd83h92h0:example.com") + <&RoomIdOrAliasId>::try_from("!29fhd83h92h0:example.com") .expect("Failed to create RoomId.") .as_ref(), "!29fhd83h92h0:example.com" @@ -165,7 +150,7 @@ mod tests { #[test] fn missing_sigil_for_room_id_or_alias_id() { assert_eq!( - RoomIdOrAliasId::try_from("ruma:example.com").unwrap_err(), + <&RoomIdOrAliasId>::try_from("ruma:example.com").unwrap_err(), Error::MissingLeadingSigil ); } @@ -175,7 +160,7 @@ mod tests { fn serialize_valid_room_id_or_alias_id_with_a_room_alias_id() { assert_eq!( serde_json::to_string( - &RoomIdOrAliasId::try_from("#ruma:example.com") + <&RoomIdOrAliasId>::try_from("#ruma:example.com") .expect("Failed to create RoomAliasId.") ) .expect("Failed to convert RoomAliasId to JSON."), @@ -188,7 +173,7 @@ mod tests { fn serialize_valid_room_id_or_alias_id_with_a_room_id() { assert_eq!( serde_json::to_string( - &RoomIdOrAliasId::try_from("!29fhd83h92h0:example.com") + <&RoomIdOrAliasId>::try_from("!29fhd83h92h0:example.com") .expect("Failed to create RoomId.") ) .expect("Failed to convert RoomId to JSON."), @@ -200,9 +185,10 @@ mod tests { #[test] fn deserialize_valid_room_id_or_alias_id_with_a_room_alias_id() { assert_eq!( - serde_json::from_str::(r##""#ruma:example.com""##) + serde_json::from_str::>(r##""#ruma:example.com""##) .expect("Failed to convert JSON to RoomAliasId"), - RoomIdOrAliasId::try_from("#ruma:example.com").expect("Failed to create RoomAliasId.") + <&RoomIdOrAliasId>::try_from("#ruma:example.com") + .expect("Failed to create RoomAliasId.") ); } @@ -210,9 +196,9 @@ mod tests { #[test] fn deserialize_valid_room_id_or_alias_id_with_a_room_id() { assert_eq!( - serde_json::from_str::(r##""!29fhd83h92h0:example.com""##) + serde_json::from_str::>(r##""!29fhd83h92h0:example.com""##) .expect("Failed to convert JSON to RoomId"), - RoomIdOrAliasId::try_from("!29fhd83h92h0:example.com") + <&RoomIdOrAliasId>::try_from("!29fhd83h92h0:example.com") .expect("Failed to create RoomAliasId.") ); } diff --git a/crates/ruma-serde-macros/src/outgoing.rs b/crates/ruma-serde-macros/src/outgoing.rs index 1af077ab..4c9e8c01 100644 --- a/crates/ruma-serde-macros/src/outgoing.rs +++ b/crates/ruma-serde-macros/src/outgoing.rs @@ -269,6 +269,7 @@ fn strip_lifetimes(field_type: &mut Type) -> bool { || last_seg.ident == "RawJsonValue" || last_seg.ident == "RoomAliasId" || last_seg.ident == "RoomId" + || last_seg.ident == "RoomIdOrAliasId" || last_seg.ident == "RoomName" { // The identifiers that need to be boxed `Box` since they are DST's.