diff --git a/crates/ruma-api/tests/manual_endpoint_impl.rs b/crates/ruma-api/tests/manual_endpoint_impl.rs index bc1d516c..7a810796 100644 --- a/crates/ruma-api/tests/manual_endpoint_impl.rs +++ b/crates/ruma-api/tests/manual_endpoint_impl.rs @@ -18,8 +18,8 @@ use serde::{Deserialize, Serialize}; /// A request to create a new room alias. #[derive(Debug)] pub struct Request { - pub room_id: RoomId, // body - pub room_alias: RoomAliasId, // path + pub room_id: RoomId, // body + pub room_alias: Box, // path } impl Outgoing for Request { diff --git a/crates/ruma-client-api/src/r0/directory.rs b/crates/ruma-client-api/src/r0/directory.rs index 8bbd08e1..05e9ffce 100644 --- a/crates/ruma-client-api/src/r0/directory.rs +++ b/crates/ruma-client-api/src/r0/directory.rs @@ -17,11 +17,11 @@ use serde::{Deserialize, Serialize}; pub struct PublicRoomsChunk { /// Aliases of the room. #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub aliases: Vec, + pub aliases: Vec>, /// The canonical alias of the room, if any. #[serde(skip_serializing_if = "Option::is_none")] - pub canonical_alias: Option, + pub canonical_alias: Option>, /// The name of the room, if any. #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/ruma-client-api/src/r0/room/aliases.rs b/crates/ruma-client-api/src/r0/room/aliases.rs index a59b2cff..9e3720e6 100644 --- a/crates/ruma-client-api/src/r0/room/aliases.rs +++ b/crates/ruma-client-api/src/r0/room/aliases.rs @@ -21,7 +21,7 @@ ruma_api! { response: { /// The server's local aliases on the room. - pub aliases: Vec, + pub aliases: Vec>, } error: crate::Error @@ -36,7 +36,7 @@ impl<'a> Request<'a> { impl Response { /// Creates a new `Response` with the given aliases. - pub fn new(aliases: Vec) -> Self { + pub fn new(aliases: Vec>) -> Self { Self { aliases } } } diff --git a/crates/ruma-client/src/lib.rs b/crates/ruma-client/src/lib.rs index 41a334ed..4701e25c 100644 --- a/crates/ruma-client/src/lib.rs +++ b/crates/ruma-client/src/lib.rs @@ -61,7 +61,7 @@ //! //! async { //! let response = client -//! .send_request(get_alias::Request::new(&room_alias_id!("#example_room:example.com"))) +//! .send_request(get_alias::Request::new(room_alias_id!("#example_room:example.com"))) //! .await?; //! //! assert_eq!(response.room_id, room_id!("!n8f893n9:example.com")); diff --git a/crates/ruma-common/src/directory.rs b/crates/ruma-common/src/directory.rs index 1b875910..8574e51c 100644 --- a/crates/ruma-common/src/directory.rs +++ b/crates/ruma-common/src/directory.rs @@ -21,11 +21,11 @@ use serde_json::Value as JsonValue; pub struct PublicRoomsChunk { /// Aliases of the room. #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub aliases: Vec, + pub aliases: Vec>, /// The canonical alias of the room, if any. #[serde(skip_serializing_if = "Option::is_none")] - pub canonical_alias: Option, + pub canonical_alias: Option>, /// The name of the room, if any. #[serde(skip_serializing_if = "Option::is_none")] diff --git a/crates/ruma-common/src/thirdparty.rs b/crates/ruma-common/src/thirdparty.rs index d52ed367..c46b7acf 100644 --- a/crates/ruma-common/src/thirdparty.rs +++ b/crates/ruma-common/src/thirdparty.rs @@ -173,7 +173,7 @@ impl From for FieldType { #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] pub struct Location { /// An alias for a matrix room. - pub alias: RoomAliasId, + pub alias: Box, /// The protocol ID that the third party location is a part of. pub protocol: String, @@ -184,7 +184,11 @@ pub struct Location { impl Location { /// Creates a new `Location` with the given alias, protocol and fields. - pub fn new(alias: RoomAliasId, protocol: String, fields: BTreeMap) -> Self { + pub fn new( + alias: Box, + protocol: String, + fields: BTreeMap, + ) -> Self { Self { alias, protocol, fields } } } diff --git a/crates/ruma-events/src/room/aliases.rs b/crates/ruma-events/src/room/aliases.rs index 65b0ca55..ee8307ca 100644 --- a/crates/ruma-events/src/room/aliases.rs +++ b/crates/ruma-events/src/room/aliases.rs @@ -18,12 +18,12 @@ use crate::{ #[ruma_event(type = "m.room.aliases", kind = State, custom_redacted)] pub struct RoomAliasesEventContent { /// A list of room aliases. - pub aliases: Vec, + pub aliases: Vec>, } impl RoomAliasesEventContent { /// Create an `RoomAliasesEventContent` from the given aliases. - pub fn new(aliases: Vec) -> Self { + pub fn new(aliases: Vec>) -> Self { Self { aliases } } } @@ -55,14 +55,14 @@ pub struct RedactedRoomAliasesEventContent { /// /// According to the Matrix spec version 1 redaction rules allowed this field to be /// kept after redaction, this was changed in version 6. - pub aliases: Option>, + pub aliases: Option>>, } impl RedactedRoomAliasesEventContent { /// Create a `RedactedAliasesEventContent` with the given aliases. /// /// This is only valid for room version 5 and below. - pub fn new_v1(aliases: Vec) -> Self { + pub fn new_v1(aliases: Vec>) -> Self { Self { aliases: Some(aliases) } } diff --git a/crates/ruma-events/src/room/canonical_alias.rs b/crates/ruma-events/src/room/canonical_alias.rs index bd6fab04..2ca05894 100644 --- a/crates/ruma-events/src/room/canonical_alias.rs +++ b/crates/ruma-events/src/room/canonical_alias.rs @@ -20,11 +20,11 @@ pub struct RoomCanonicalAliasEventContent { deserialize_with = "ruma_serde::empty_string_as_none", skip_serializing_if = "Option::is_none" )] - pub alias: Option, + pub alias: Option>, /// List of alternative aliases to the room. #[serde(default, skip_serializing_if = "Vec::is_empty")] - pub alt_aliases: Vec, + pub alt_aliases: Vec>, } impl RoomCanonicalAliasEventContent { @@ -48,7 +48,7 @@ mod tests { fn serialization_with_optional_fields_as_none() { let canonical_alias_event = StateEvent { content: RoomCanonicalAliasEventContent { - alias: Some(room_alias_id!("#somewhere:localhost")), + alias: Some(room_alias_id!("#somewhere:localhost").to_owned()), alt_aliases: Vec::new(), }, event_id: event_id!("$h29iv0s8:example.com").to_owned(), @@ -143,7 +143,7 @@ mod tests { #[test] fn nonempty_field_as_some() { - let alias = Some(room_alias_id!("#somewhere:localhost")); + let alias = Some(room_alias_id!("#somewhere:localhost").to_owned()); let json_data = json!({ "content": { "alias": "#somewhere:localhost" diff --git a/crates/ruma-events/tests/state_event.rs b/crates/ruma-events/tests/state_event.rs index 6efea980..e35d608a 100644 --- a/crates/ruma-events/tests/state_event.rs +++ b/crates/ruma-events/tests/state_event.rs @@ -36,10 +36,14 @@ fn aliases_event_with_prev_content() -> JsonValue { #[test] fn serialize_aliases_with_prev_content() { let aliases_event = StateEvent { - content: RoomAliasesEventContent::new(vec![room_alias_id!("#somewhere:localhost")]), + content: RoomAliasesEventContent::new(vec![ + room_alias_id!("#somewhere:localhost").to_owned() + ]), event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), - prev_content: Some(RoomAliasesEventContent::new(vec![room_alias_id!("#inner:localhost")])), + prev_content: Some(RoomAliasesEventContent::new(vec![ + room_alias_id!("#inner:localhost").to_owned() + ])), room_id: room_id!("!roomid:room.com"), sender: user_id!("@carl:example.com"), state_key: "".into(), @@ -55,7 +59,9 @@ fn serialize_aliases_with_prev_content() { #[test] fn serialize_aliases_without_prev_content() { let aliases_event = StateEvent { - content: RoomAliasesEventContent::new(vec![room_alias_id!("#somewhere:localhost")]), + content: RoomAliasesEventContent::new(vec![ + room_alias_id!("#somewhere:localhost").to_owned() + ]), event_id: event_id!("$h29iv0s8:example.com").to_owned(), origin_server_ts: MilliSecondsSinceUnixEpoch(uint!(1)), prev_content: None, diff --git a/crates/ruma-identifiers-macros/src/lib.rs b/crates/ruma-identifiers-macros/src/lib.rs index 467e9ec3..f34de542 100644 --- a/crates/ruma-identifiers-macros/src/lib.rs +++ b/crates/ruma-identifiers-macros/src/lib.rs @@ -55,7 +55,7 @@ pub fn room_alias_id(input: TokenStream) -> TokenStream { assert!(room_alias_id::validate(&id.value()).is_ok(), "Invalid room_alias_id"); let output = quote! { - <#dollar_crate::RoomAliasId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() + <&#dollar_crate::RoomAliasId as ::std::convert::TryFrom<&str>>::try_from(#id).unwrap() }; output.into() diff --git a/crates/ruma-identifiers-validation/src/room_alias_id.rs b/crates/ruma-identifiers-validation/src/room_alias_id.rs index 48ab1220..d1ecfb88 100644 --- a/crates/ruma-identifiers-validation/src/room_alias_id.rs +++ b/crates/ruma-identifiers-validation/src/room_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_alias_id.rs b/crates/ruma-identifiers/src/room_alias_id.rs index 7ecaea10..46a09b6e 100644 --- a/crates/ruma-identifiers/src/room_alias_id.rs +++ b/crates/ruma-identifiers/src/room_alias_id.rs @@ -1,6 +1,6 @@ //! Matrix room alias identifiers. -use std::{convert::TryInto, fmt, num::NonZeroU8}; +use std::convert::TryInto; use crate::{server_name::ServerName, EventId, MatrixToRef}; @@ -13,57 +13,41 @@ use crate::{server_name::ServerName, EventId, MatrixToRef}; /// # use std::convert::TryFrom; /// # use ruma_identifiers::RoomAliasId; /// assert_eq!( -/// RoomAliasId::try_from("#ruma:example.com").unwrap().as_ref(), +/// <&RoomAliasId>::try_from("#ruma:example.com").unwrap(), /// "#ruma:example.com" /// ); /// ``` -#[derive(Clone)] -pub struct RoomAliasId { - pub(crate) full_id: Box, - pub(crate) colon_idx: NonZeroU8, -} +#[repr(transparent)] +pub struct RoomAliasId(str); -impl fmt::Debug for RoomAliasId { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.full_id.fmt(f) - } -} +opaque_identifier_validated!(RoomAliasId, ruma_identifiers_validation::room_alias_id::validate); impl RoomAliasId { /// Returns the room's alias. pub fn alias(&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() } /// Create a `matrix.to` reference for this room alias ID. pub fn matrix_to_url(&self) -> MatrixToRef<'_> { - MatrixToRef::new(&self.full_id, Vec::new()) + MatrixToRef::new(self.as_str(), Vec::new()) } /// Create a `matrix.to` reference for an event scoped under this room alias ID. pub fn matrix_to_event_url<'a>(&'a self, ev_id: &'a EventId) -> MatrixToRef<'a> { - MatrixToRef::event(&self.full_id, ev_id, Vec::new()) + MatrixToRef::event(self.as_str(), ev_id, Vec::new()) + } + + fn colon_idx(&self) -> usize { + self.as_str().find(':').unwrap() } } -/// Attempts to create a new Matrix room alias ID from a string representation. -/// -/// The string must include the leading # sigil, the alias, a literal colon, and a server name. -fn try_from(room_alias_id: S) -> Result -where - S: AsRef + Into>, -{ - let colon_idx = ruma_identifiers_validation::room_alias_id::validate(room_alias_id.as_ref())?; - Ok(RoomAliasId { full_id: room_alias_id.into(), colon_idx }) -} - -common_impls!(RoomAliasId, try_from, "a Matrix room alias ID"); - #[cfg(test)] mod tests { use std::convert::TryFrom; @@ -74,9 +58,7 @@ mod tests { #[test] fn valid_room_alias_id() { assert_eq!( - RoomAliasId::try_from("#ruma:example.com") - .expect("Failed to create RoomAliasId.") - .as_ref(), + <&RoomAliasId>::try_from("#ruma:example.com").expect("Failed to create RoomAliasId."), "#ruma:example.com" ); } @@ -84,9 +66,7 @@ mod tests { #[test] fn empty_localpart() { assert_eq!( - RoomAliasId::try_from("#:myhomeserver.io") - .expect("Failed to create RoomAliasId.") - .as_ref(), + <&RoomAliasId>::try_from("#:myhomeserver.io").expect("Failed to create RoomAliasId."), "#:myhomeserver.io" ); } @@ -96,7 +76,8 @@ mod tests { fn serialize_valid_room_alias_id() { assert_eq!( serde_json::to_string( - &RoomAliasId::try_from("#ruma:example.com").expect("Failed to create RoomAliasId.") + <&RoomAliasId>::try_from("#ruma:example.com") + .expect("Failed to create RoomAliasId.") ) .expect("Failed to convert RoomAliasId to JSON."), r##""#ruma:example.com""## @@ -107,18 +88,17 @@ mod tests { #[test] fn deserialize_valid_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"), - RoomAliasId::try_from("#ruma:example.com").expect("Failed to create RoomAliasId.") + <&RoomAliasId>::try_from("#ruma:example.com").expect("Failed to create RoomAliasId.") ); } #[test] fn valid_room_alias_id_with_explicit_standard_port() { assert_eq!( - RoomAliasId::try_from("#ruma:example.com:443") - .expect("Failed to create RoomAliasId.") - .as_ref(), + <&RoomAliasId>::try_from("#ruma:example.com:443") + .expect("Failed to create RoomAliasId."), "#ruma:example.com:443" ); } @@ -126,9 +106,8 @@ mod tests { #[test] fn valid_room_alias_id_with_non_standard_port() { assert_eq!( - RoomAliasId::try_from("#ruma:example.com:5000") - .expect("Failed to create RoomAliasId.") - .as_ref(), + <&RoomAliasId>::try_from("#ruma:example.com:5000") + .expect("Failed to create RoomAliasId."), "#ruma:example.com:5000" ); } @@ -136,9 +115,8 @@ mod tests { #[test] fn valid_room_alias_id_unicode() { assert_eq!( - RoomAliasId::try_from("#老虎£я:example.com") - .expect("Failed to create RoomAliasId.") - .as_ref(), + <&RoomAliasId>::try_from("#老虎£я:example.com") + .expect("Failed to create RoomAliasId."), "#老虎£я:example.com" ); } @@ -146,33 +124,33 @@ mod tests { #[test] fn missing_room_alias_id_sigil() { assert_eq!( - RoomAliasId::try_from("39hvsi03hlne:example.com").unwrap_err(), + <&RoomAliasId>::try_from("39hvsi03hlne:example.com").unwrap_err(), Error::MissingLeadingSigil ); } #[test] fn missing_room_alias_id_delimiter() { - assert_eq!(RoomAliasId::try_from("#ruma").unwrap_err(), Error::MissingDelimiter); + assert_eq!(<&RoomAliasId>::try_from("#ruma").unwrap_err(), Error::MissingDelimiter); } #[test] fn invalid_leading_sigil() { assert_eq!( - RoomAliasId::try_from("!room_id:foo.bar").unwrap_err(), + <&RoomAliasId>::try_from("!room_id:foo.bar").unwrap_err(), Error::MissingLeadingSigil ); } #[test] fn invalid_room_alias_id_host() { - assert_eq!(RoomAliasId::try_from("#ruma:/").unwrap_err(), Error::InvalidServerName); + assert_eq!(<&RoomAliasId>::try_from("#ruma:/").unwrap_err(), Error::InvalidServerName); } #[test] fn invalid_room_alias_id_port() { assert_eq!( - RoomAliasId::try_from("#ruma:example.com:notaport").unwrap_err(), + <&RoomAliasId>::try_from("#ruma:example.com:notaport").unwrap_err(), Error::InvalidServerName ); } 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 27d7accc..40ed6f55 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 @@ -63,15 +63,12 @@ impl RoomIdOrAliasId { /// Turn this `RoomIdOrAliasId` into `Either` #[cfg(feature = "either")] - pub fn into_either(self) -> either::Either { + pub fn into_either(self) -> either::Either> { match self.variant() { Variant::RoomId => { either::Either::Left(RoomId { full_id: self.full_id, colon_idx: self.colon_idx }) } - Variant::RoomAliasId => either::Either::Right(RoomAliasId { - full_id: self.full_id, - colon_idx: self.colon_idx, - }), + Variant::RoomAliasId => either::Either::Right(self.as_str().try_into().unwrap()), } } @@ -112,33 +109,29 @@ impl From for RoomIdOrAliasId { } } -impl From for RoomIdOrAliasId { - fn from(RoomAliasId { full_id, colon_idx }: RoomAliasId) -> Self { - Self { full_id, colon_idx } +impl From> for RoomIdOrAliasId { + fn from(room_alias_id: Box) -> Self { + Self::try_from(room_alias_id.as_str()).unwrap() } } impl TryFrom for RoomId { - type Error = RoomAliasId; + type Error = Box; - fn try_from(id: RoomIdOrAliasId) -> Result { + fn try_from(id: RoomIdOrAliasId) -> Result> { match id.variant() { Variant::RoomId => Ok(RoomId { full_id: id.full_id, colon_idx: id.colon_idx }), - Variant::RoomAliasId => { - Err(RoomAliasId { full_id: id.full_id, colon_idx: id.colon_idx }) - } + Variant::RoomAliasId => Err(id.as_str().try_into().unwrap()), } } } -impl TryFrom for RoomAliasId { +impl TryFrom for Box { type Error = RoomId; - fn try_from(id: RoomIdOrAliasId) -> Result { + fn try_from(id: RoomIdOrAliasId) -> Result, RoomId> { match id.variant() { - Variant::RoomAliasId => { - Ok(RoomAliasId { full_id: id.full_id, colon_idx: id.colon_idx }) - } + Variant::RoomAliasId => Ok(id.as_str().try_into().unwrap()), Variant::RoomId => Err(RoomId { full_id: id.full_id, colon_idx: id.colon_idx }), } } diff --git a/crates/ruma-push-gateway-api/src/send_event_notification/v1.rs b/crates/ruma-push-gateway-api/src/send_event_notification/v1.rs index ade8e49e..50c2866a 100644 --- a/crates/ruma-push-gateway-api/src/send_event_notification/v1.rs +++ b/crates/ruma-push-gateway-api/src/send_event_notification/v1.rs @@ -368,7 +368,7 @@ mod tests { event_type: Some(&EventType::RoomMessage), sender: Some(&uid), sender_display_name: Some("Major Tom"), - room_alias: Some(&alias), + room_alias: Some(alias), content: Some(serde_json::from_str("{}").unwrap()), counts: count, prio: NotificationPriority::Low, diff --git a/crates/ruma-serde-macros/src/outgoing.rs b/crates/ruma-serde-macros/src/outgoing.rs index 9ba1117f..835bd141 100644 --- a/crates/ruma-serde-macros/src/outgoing.rs +++ b/crates/ruma-serde-macros/src/outgoing.rs @@ -267,6 +267,7 @@ fn strip_lifetimes(field_type: &mut Type) -> bool { || last_seg.ident == "ServerName" || last_seg.ident == "SessionId" || last_seg.ident == "RawJsonValue" + || last_seg.ident == "RoomAliasId" || last_seg.ident == "RoomName" { // The identifiers that need to be boxed `Box` since they are DST's. diff --git a/crates/ruma/examples/hello_isahc.rs b/crates/ruma/examples/hello_isahc.rs index ec4fd8f1..16e55db0 100644 --- a/crates/ruma/examples/hello_isahc.rs +++ b/crates/ruma/examples/hello_isahc.rs @@ -48,5 +48,6 @@ async fn main() -> anyhow::Result<()> { } }; - hello_world(homeserver_url, &username, &password, &RoomAliasId::try_from(room.as_str())?).await + hello_world(homeserver_url, &username, &password, <&RoomAliasId>::try_from(room.as_str())?) + .await } diff --git a/crates/ruma/examples/hello_world.rs b/crates/ruma/examples/hello_world.rs index e2e40197..8960af8a 100644 --- a/crates/ruma/examples/hello_world.rs +++ b/crates/ruma/examples/hello_world.rs @@ -44,5 +44,6 @@ async fn main() -> anyhow::Result<()> { } }; - hello_world(homeserver_url, &username, &password, &RoomAliasId::try_from(room.as_str())?).await + hello_world(homeserver_url, &username, &password, <&RoomAliasId>::try_from(room.as_str())?) + .await }