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 "$39hvsi03hlne:example.com"
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 "$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk"
215 );
216 }
217
218 #[test]
219 fn deserialize_valid_url_safe_base64_event_id() {
220 assert_eq!(
221 serde_json::from_str::<OwnedEventId>(
222 r#""$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg""#
223 )
224 .expect("Failed to convert JSON to EventId"),
225 "$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg"
226 );
227 }
228
229 #[test]
230 fn valid_original_event_id_with_explicit_standard_port() {
231 assert_eq!(
232 <&EventId>::try_from("$39hvsi03hlne:example.com:443")
233 .expect("Failed to create EventId."),
234 "$39hvsi03hlne:example.com:443"
235 );
236 }
237
238 #[test]
239 fn valid_original_event_id_with_non_standard_port() {
240 assert_eq!(
241 <&EventId>::try_from("$39hvsi03hlne:example.com:5000")
242 .expect("Failed to create EventId."),
243 "$39hvsi03hlne:example.com:5000"
244 );
245 }
246
247 #[test]
248 fn missing_original_event_id_sigil() {
249 assert_eq!(
250 <&EventId>::try_from("39hvsi03hlne:example.com").unwrap_err(),
251 IdParseError::MissingLeadingSigil
252 );
253 }
254
255 #[test]
256 fn missing_base64_event_id_sigil() {
257 assert_eq!(
258 <&EventId>::try_from("acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk").unwrap_err(),
259 IdParseError::MissingLeadingSigil
260 );
261 }
262
263 #[test]
264 fn missing_url_safe_base64_event_id_sigil() {
265 assert_eq!(
266 <&EventId>::try_from("Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg").unwrap_err(),
267 IdParseError::MissingLeadingSigil
268 );
269 }
270
271 #[test]
272 fn invalid_event_id_host() {
273 assert_eq!(
274 <&EventId>::try_from("$39hvsi03hlne:/").unwrap_err(),
275 IdParseError::InvalidServerName
276 );
277 }
278
279 #[test]
280 fn invalid_event_id_port() {
281 assert_eq!(
282 <&EventId>::try_from("$39hvsi03hlne:example.com:notaport").unwrap_err(),
283 IdParseError::InvalidServerName
284 );
285 }
286
287 #[test]
288 fn construct_v2_or_v3_event_id() {
289 assert_eq!(
290 EventId::new_v2_or_v3("Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg").unwrap(),
291 "$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg"
292 );
293 }
294}