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)]
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 let idx = self.colon_idx().unwrap_or_else(|| self.as_str().len());
107 &self.as_str()[1..idx]
108 }
109
110 pub fn server_name(&self) -> Option<&ServerName> {
114 self.colon_idx().map(|idx| ServerName::from_borrowed_unchecked(&self.as_str()[idx + 1..]))
115 }
116
117 fn colon_idx(&self) -> Option<usize> {
118 self.as_str().find(':')
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::{EventId, OwnedEventId};
125 use crate::IdParseError;
126
127 #[test]
128 fn valid_original_event_id() {
129 assert_eq!(
130 <&EventId>::try_from("$39hvsi03hlne:example.com").expect("Failed to create EventId."),
131 "$39hvsi03hlne:example.com"
132 );
133 }
134
135 #[test]
136 fn valid_base64_event_id() {
137 assert_eq!(
138 <&EventId>::try_from("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk")
139 .expect("Failed to create EventId."),
140 "$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk"
141 );
142 }
143
144 #[test]
145 fn valid_url_safe_base64_event_id() {
146 assert_eq!(
147 <&EventId>::try_from("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg")
148 .expect("Failed to create EventId."),
149 "$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg"
150 );
151 }
152
153 #[cfg(feature = "rand")]
154 #[test]
155 fn generate_random_valid_event_id() {
156 use crate::server_name;
157
158 let server_name = server_name!("example.com");
159 let event_id = EventId::new_v1(server_name);
160 let id_str = event_id.as_str();
161
162 assert!(id_str.starts_with('$'));
163 assert_eq!(id_str.len(), 31);
164 assert_eq!(event_id.server_name(), Some(server_name));
165 }
166
167 #[test]
168 fn serialize_valid_original_event_id() {
169 assert_eq!(
170 serde_json::to_string(
171 <&EventId>::try_from("$39hvsi03hlne:example.com")
172 .expect("Failed to create EventId.")
173 )
174 .expect("Failed to convert EventId to JSON."),
175 r#""$39hvsi03hlne:example.com""#
176 );
177 }
178
179 #[test]
180 fn serialize_valid_base64_event_id() {
181 assert_eq!(
182 serde_json::to_string(
183 <&EventId>::try_from("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk")
184 .expect("Failed to create EventId.")
185 )
186 .expect("Failed to convert EventId to JSON."),
187 r#""$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk""#
188 );
189 }
190
191 #[test]
192 fn serialize_valid_url_safe_base64_event_id() {
193 assert_eq!(
194 serde_json::to_string(
195 <&EventId>::try_from("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg")
196 .expect("Failed to create EventId.")
197 )
198 .expect("Failed to convert EventId to JSON."),
199 r#""$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg""#
200 );
201 }
202
203 #[test]
204 fn deserialize_valid_original_event_id() {
205 assert_eq!(
206 serde_json::from_str::<OwnedEventId>(r#""$39hvsi03hlne:example.com""#)
207 .expect("Failed to convert JSON to EventId"),
208 <&EventId>::try_from("$39hvsi03hlne:example.com").expect("Failed to create EventId.")
209 );
210 }
211
212 #[test]
213 fn deserialize_valid_base64_event_id() {
214 assert_eq!(
215 serde_json::from_str::<OwnedEventId>(
216 r#""$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk""#
217 )
218 .expect("Failed to convert JSON to EventId"),
219 <&EventId>::try_from("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk")
220 .expect("Failed to create EventId.")
221 );
222 }
223
224 #[test]
225 fn deserialize_valid_url_safe_base64_event_id() {
226 assert_eq!(
227 serde_json::from_str::<OwnedEventId>(
228 r#""$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg""#
229 )
230 .expect("Failed to convert JSON to EventId"),
231 <&EventId>::try_from("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg")
232 .expect("Failed to create EventId.")
233 );
234 }
235
236 #[test]
237 fn valid_original_event_id_with_explicit_standard_port() {
238 assert_eq!(
239 <&EventId>::try_from("$39hvsi03hlne:example.com:443")
240 .expect("Failed to create EventId."),
241 "$39hvsi03hlne:example.com:443"
242 );
243 }
244
245 #[test]
246 fn valid_original_event_id_with_non_standard_port() {
247 assert_eq!(
248 <&EventId>::try_from("$39hvsi03hlne:example.com:5000")
249 .expect("Failed to create EventId."),
250 "$39hvsi03hlne:example.com:5000"
251 );
252 }
253
254 #[test]
255 fn missing_original_event_id_sigil() {
256 assert_eq!(
257 <&EventId>::try_from("39hvsi03hlne:example.com").unwrap_err(),
258 IdParseError::MissingLeadingSigil
259 );
260 }
261
262 #[test]
263 fn missing_base64_event_id_sigil() {
264 assert_eq!(
265 <&EventId>::try_from("acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk").unwrap_err(),
266 IdParseError::MissingLeadingSigil
267 );
268 }
269
270 #[test]
271 fn missing_url_safe_base64_event_id_sigil() {
272 assert_eq!(
273 <&EventId>::try_from("Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg").unwrap_err(),
274 IdParseError::MissingLeadingSigil
275 );
276 }
277
278 #[test]
279 fn invalid_event_id_host() {
280 assert_eq!(
281 <&EventId>::try_from("$39hvsi03hlne:/").unwrap_err(),
282 IdParseError::InvalidServerName
283 );
284 }
285
286 #[test]
287 fn invalid_event_id_port() {
288 assert_eq!(
289 <&EventId>::try_from("$39hvsi03hlne:example.com:notaport").unwrap_err(),
290 IdParseError::InvalidServerName
291 );
292 }
293
294 #[test]
295 fn construct_v2_or_v3_event_id() {
296 assert_eq!(
297 EventId::new_v2_or_v3("Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg").unwrap(),
298 "$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg"
299 );
300 }
301}