ruma_common/identifiers/
room_alias_id.rs

1//! Matrix room alias identifiers.
2
3use ruma_macros::IdZst;
4
5use super::{matrix_uri::UriAction, server_name::ServerName, MatrixToUri, MatrixUri, OwnedEventId};
6
7/// A Matrix [room alias ID].
8///
9/// A `RoomAliasId` is converted from a string slice, and can be converted back into a string as
10/// needed.
11///
12/// ```
13/// # use ruma_common::RoomAliasId;
14/// assert_eq!(<&RoomAliasId>::try_from("#ruma:example.com").unwrap(), "#ruma:example.com");
15/// ```
16///
17/// [room alias ID]: https://spec.matrix.org/latest/appendices/#room-aliases
18#[repr(transparent)]
19#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdZst)]
20#[ruma_id(validate = ruma_identifiers_validation::room_alias_id::validate)]
21pub struct RoomAliasId(str);
22
23impl RoomAliasId {
24    /// Returns the room's alias.
25    pub fn alias(&self) -> &str {
26        &self.as_str()[1..self.colon_idx()]
27    }
28
29    /// Returns the server name of the room alias ID.
30    pub fn server_name(&self) -> &ServerName {
31        ServerName::from_borrowed(&self.as_str()[self.colon_idx() + 1..])
32    }
33
34    /// Create a `matrix.to` URI for this room alias ID.
35    pub fn matrix_to_uri(&self) -> MatrixToUri {
36        MatrixToUri::new(self.into(), Vec::new())
37    }
38
39    /// Create a `matrix.to` URI for an event scoped under this room alias ID.
40    ///
41    /// This is deprecated because room aliases are mutable, so the URI might break after a while.
42    #[deprecated = "Use `RoomId::matrix_to_event_uri` instead."]
43    pub fn matrix_to_event_uri(&self, ev_id: impl Into<OwnedEventId>) -> MatrixToUri {
44        MatrixToUri::new((self.to_owned(), ev_id.into()).into(), Vec::new())
45    }
46
47    /// Create a `matrix:` URI for this room alias ID.
48    ///
49    /// If `join` is `true`, a click on the URI should join the room.
50    pub fn matrix_uri(&self, join: bool) -> MatrixUri {
51        MatrixUri::new(self.into(), Vec::new(), Some(UriAction::Join).filter(|_| join))
52    }
53
54    /// Create a `matrix:` URI for an event scoped under this room alias ID.
55    ///
56    /// This is deprecated because room aliases are mutable, so the URI might break after a while.
57    #[deprecated = "Use `RoomId::matrix_event_uri` instead."]
58    pub fn matrix_event_uri(&self, ev_id: impl Into<OwnedEventId>) -> MatrixUri {
59        MatrixUri::new((self.to_owned(), ev_id.into()).into(), Vec::new(), None)
60    }
61
62    fn colon_idx(&self) -> usize {
63        self.as_str().find(':').unwrap()
64    }
65}
66
67#[cfg(test)]
68mod tests {
69    use super::{OwnedRoomAliasId, RoomAliasId};
70    use crate::IdParseError;
71
72    #[test]
73    fn valid_room_alias_id() {
74        assert_eq!(
75            <&RoomAliasId>::try_from("#ruma:example.com").expect("Failed to create RoomAliasId."),
76            "#ruma:example.com"
77        );
78    }
79
80    #[test]
81    fn empty_localpart() {
82        assert_eq!(
83            <&RoomAliasId>::try_from("#:myhomeserver.io").expect("Failed to create RoomAliasId."),
84            "#:myhomeserver.io"
85        );
86    }
87
88    #[test]
89    fn serialize_valid_room_alias_id() {
90        assert_eq!(
91            serde_json::to_string(
92                <&RoomAliasId>::try_from("#ruma:example.com")
93                    .expect("Failed to create RoomAliasId.")
94            )
95            .expect("Failed to convert RoomAliasId to JSON."),
96            r##""#ruma:example.com""##
97        );
98    }
99
100    #[test]
101    fn deserialize_valid_room_alias_id() {
102        assert_eq!(
103            serde_json::from_str::<OwnedRoomAliasId>(r##""#ruma:example.com""##)
104                .expect("Failed to convert JSON to RoomAliasId"),
105            <&RoomAliasId>::try_from("#ruma:example.com").expect("Failed to create RoomAliasId.")
106        );
107    }
108
109    #[test]
110    fn valid_room_alias_id_with_explicit_standard_port() {
111        assert_eq!(
112            <&RoomAliasId>::try_from("#ruma:example.com:443")
113                .expect("Failed to create RoomAliasId."),
114            "#ruma:example.com:443"
115        );
116    }
117
118    #[test]
119    fn valid_room_alias_id_with_non_standard_port() {
120        assert_eq!(
121            <&RoomAliasId>::try_from("#ruma:example.com:5000")
122                .expect("Failed to create RoomAliasId."),
123            "#ruma:example.com:5000"
124        );
125    }
126
127    #[test]
128    fn valid_room_alias_id_unicode() {
129        assert_eq!(
130            <&RoomAliasId>::try_from("#老虎£я:example.com")
131                .expect("Failed to create RoomAliasId."),
132            "#老虎£я:example.com"
133        );
134    }
135
136    #[test]
137    fn missing_room_alias_id_sigil() {
138        assert_eq!(
139            <&RoomAliasId>::try_from("39hvsi03hlne:example.com").unwrap_err(),
140            IdParseError::MissingLeadingSigil
141        );
142    }
143
144    #[test]
145    fn missing_room_alias_id_delimiter() {
146        assert_eq!(<&RoomAliasId>::try_from("#ruma").unwrap_err(), IdParseError::MissingColon);
147    }
148
149    #[test]
150    fn invalid_leading_sigil() {
151        assert_eq!(
152            <&RoomAliasId>::try_from("!room_id:foo.bar").unwrap_err(),
153            IdParseError::MissingLeadingSigil
154        );
155    }
156
157    #[test]
158    fn invalid_room_alias_id_host() {
159        assert_eq!(
160            <&RoomAliasId>::try_from("#ruma:/").unwrap_err(),
161            IdParseError::InvalidServerName
162        );
163    }
164
165    #[test]
166    fn invalid_room_alias_id_port() {
167        assert_eq!(
168            <&RoomAliasId>::try_from("#ruma:example.com:notaport").unwrap_err(),
169            IdParseError::InvalidServerName
170        );
171    }
172}