ruma_common/identifiers/
event_id.rs1use ruma_macros::IdDst;
4
5use super::{IdParseError, ServerName};
6
7#[repr(transparent)]
57#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdDst)]
58#[ruma_id(validate = ruma_identifiers_validation::event_id::validate, smallvec_inline_bytes = 48)]
59pub struct EventId(str);
60
61impl EventId {
62 #[cfg(feature = "rand")]
75 #[allow(clippy::new_ret_no_self)]
76 pub fn new_v1(server_name: &ServerName) -> OwnedEventId {
77 OwnedEventId::from_string_unchecked(format!(
78 "${}:{server_name}",
79 super::generate_localpart(18)
80 ))
81 }
82
83 pub fn new_v2_or_v3(reference_hash: &str) -> Result<OwnedEventId, IdParseError> {
97 OwnedEventId::try_from(format!("${reference_hash}"))
98 }
99
100 pub fn localpart(&self) -> &str {
106 super::find_localpart(self.as_str())
107 }
108
109 pub fn server_name(&self) -> Option<&ServerName> {
113 super::find_server_name_unchecked(self.as_str())
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::{EventId, OwnedEventId};
120 use crate::IdParseError;
121
122 #[test]
123 fn valid_original_event_id() {
124 assert_eq!(
125 <&EventId>::try_from("$39hvsi03hlne:example.com").expect("Failed to create EventId."),
126 "$39hvsi03hlne:example.com"
127 );
128 }
129
130 #[test]
131 fn valid_base64_event_id() {
132 assert_eq!(
133 <&EventId>::try_from("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk")
134 .expect("Failed to create EventId."),
135 "$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk"
136 );
137 }
138
139 #[test]
140 fn valid_url_safe_base64_event_id() {
141 assert_eq!(
142 <&EventId>::try_from("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg")
143 .expect("Failed to create EventId."),
144 "$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg"
145 );
146 }
147
148 #[cfg(feature = "rand")]
149 #[test]
150 fn generate_random_valid_event_id() {
151 use crate::server_name;
152
153 let server_name = server_name!("example.com");
154 let event_id = EventId::new_v1(server_name);
155 let id_str = event_id.as_str();
156
157 assert!(id_str.starts_with('$'));
158 assert_eq!(id_str.len(), 31);
159 assert_eq!(event_id.server_name(), Some(server_name));
160 }
161
162 #[test]
163 fn serialize_valid_original_event_id() {
164 assert_eq!(
165 serde_json::to_string(
166 <&EventId>::try_from("$39hvsi03hlne:example.com")
167 .expect("Failed to create EventId.")
168 )
169 .expect("Failed to convert EventId to JSON."),
170 r#""$39hvsi03hlne:example.com""#
171 );
172 }
173
174 #[test]
175 fn serialize_valid_base64_event_id() {
176 assert_eq!(
177 serde_json::to_string(
178 <&EventId>::try_from("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk")
179 .expect("Failed to create EventId.")
180 )
181 .expect("Failed to convert EventId to JSON."),
182 r#""$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk""#
183 );
184 }
185
186 #[test]
187 fn serialize_valid_url_safe_base64_event_id() {
188 assert_eq!(
189 serde_json::to_string(
190 <&EventId>::try_from("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg")
191 .expect("Failed to create EventId.")
192 )
193 .expect("Failed to convert EventId to JSON."),
194 r#""$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg""#
195 );
196 }
197
198 #[test]
199 fn deserialize_valid_original_event_id() {
200 assert_eq!(
201 serde_json::from_str::<OwnedEventId>(r#""$39hvsi03hlne:example.com""#)
202 .expect("Failed to convert JSON to EventId"),
203 <&EventId>::try_from("$39hvsi03hlne:example.com").expect("Failed to create EventId.")
204 );
205 }
206
207 #[test]
208 fn deserialize_valid_base64_event_id() {
209 assert_eq!(
210 serde_json::from_str::<OwnedEventId>(
211 r#""$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk""#
212 )
213 .expect("Failed to convert JSON to EventId"),
214 <&EventId>::try_from("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk")
215 .expect("Failed to create EventId.")
216 );
217 }
218
219 #[test]
220 fn deserialize_valid_url_safe_base64_event_id() {
221 assert_eq!(
222 serde_json::from_str::<OwnedEventId>(
223 r#""$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg""#
224 )
225 .expect("Failed to convert JSON to EventId"),
226 <&EventId>::try_from("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg")
227 .expect("Failed to create EventId.")
228 );
229 }
230
231 #[test]
232 fn valid_original_event_id_with_explicit_standard_port() {
233 assert_eq!(
234 <&EventId>::try_from("$39hvsi03hlne:example.com:443")
235 .expect("Failed to create EventId."),
236 "$39hvsi03hlne:example.com:443"
237 );
238 }
239
240 #[test]
241 fn valid_original_event_id_with_non_standard_port() {
242 assert_eq!(
243 <&EventId>::try_from("$39hvsi03hlne:example.com:5000")
244 .expect("Failed to create EventId."),
245 "$39hvsi03hlne:example.com:5000"
246 );
247 }
248
249 #[test]
250 fn missing_original_event_id_sigil() {
251 assert_eq!(
252 <&EventId>::try_from("39hvsi03hlne:example.com").unwrap_err(),
253 IdParseError::MissingLeadingSigil
254 );
255 }
256
257 #[test]
258 fn missing_base64_event_id_sigil() {
259 assert_eq!(
260 <&EventId>::try_from("acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk").unwrap_err(),
261 IdParseError::MissingLeadingSigil
262 );
263 }
264
265 #[test]
266 fn missing_url_safe_base64_event_id_sigil() {
267 assert_eq!(
268 <&EventId>::try_from("Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg").unwrap_err(),
269 IdParseError::MissingLeadingSigil
270 );
271 }
272
273 #[test]
274 fn invalid_event_id_host() {
275 assert_eq!(
276 <&EventId>::try_from("$39hvsi03hlne:/").unwrap_err(),
277 IdParseError::InvalidServerName
278 );
279 }
280
281 #[test]
282 fn invalid_event_id_port() {
283 assert_eq!(
284 <&EventId>::try_from("$39hvsi03hlne:example.com:notaport").unwrap_err(),
285 IdParseError::InvalidServerName
286 );
287 }
288
289 #[test]
290 fn construct_v2_or_v3_event_id() {
291 assert_eq!(
292 EventId::new_v2_or_v3("Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg").unwrap(),
293 "$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg"
294 );
295 }
296}