ruma_common/identifiers/
event_id.rs1use ruma_macros::IdDst;
4
5use super::ServerName;
6
7#[repr(transparent)]
39#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, IdDst)]
40#[ruma_id(validate = ruma_identifiers_validation::event_id::validate)]
41pub struct EventId(str);
42
43impl EventId {
44    #[cfg(feature = "rand")]
50    #[allow(clippy::new_ret_no_self)]
51    pub fn new(server_name: &ServerName) -> OwnedEventId {
52        Self::from_borrowed(&format!("${}:{server_name}", super::generate_localpart(18))).to_owned()
53    }
54
55    pub fn localpart(&self) -> &str {
61        let idx = self.colon_idx().unwrap_or_else(|| self.as_str().len());
62        &self.as_str()[1..idx]
63    }
64
65    pub fn server_name(&self) -> Option<&ServerName> {
69        self.colon_idx().map(|idx| ServerName::from_borrowed(&self.as_str()[idx + 1..]))
70    }
71
72    fn colon_idx(&self) -> Option<usize> {
73        self.as_str().find(':')
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use super::{EventId, OwnedEventId};
80    use crate::IdParseError;
81
82    #[test]
83    fn valid_original_event_id() {
84        assert_eq!(
85            <&EventId>::try_from("$39hvsi03hlne:example.com").expect("Failed to create EventId."),
86            "$39hvsi03hlne:example.com"
87        );
88    }
89
90    #[test]
91    fn valid_base64_event_id() {
92        assert_eq!(
93            <&EventId>::try_from("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk")
94                .expect("Failed to create EventId."),
95            "$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk"
96        );
97    }
98
99    #[test]
100    fn valid_url_safe_base64_event_id() {
101        assert_eq!(
102            <&EventId>::try_from("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg")
103                .expect("Failed to create EventId."),
104            "$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg"
105        );
106    }
107
108    #[cfg(feature = "rand")]
109    #[test]
110    fn generate_random_valid_event_id() {
111        use crate::server_name;
112
113        let event_id = EventId::new(server_name!("example.com"));
114        let id_str = event_id.as_str();
115
116        assert!(id_str.starts_with('$'));
117        assert_eq!(id_str.len(), 31);
118    }
119
120    #[test]
121    fn serialize_valid_original_event_id() {
122        assert_eq!(
123            serde_json::to_string(
124                <&EventId>::try_from("$39hvsi03hlne:example.com")
125                    .expect("Failed to create EventId.")
126            )
127            .expect("Failed to convert EventId to JSON."),
128            r#""$39hvsi03hlne:example.com""#
129        );
130    }
131
132    #[test]
133    fn serialize_valid_base64_event_id() {
134        assert_eq!(
135            serde_json::to_string(
136                <&EventId>::try_from("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk")
137                    .expect("Failed to create EventId.")
138            )
139            .expect("Failed to convert EventId to JSON."),
140            r#""$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk""#
141        );
142    }
143
144    #[test]
145    fn serialize_valid_url_safe_base64_event_id() {
146        assert_eq!(
147            serde_json::to_string(
148                <&EventId>::try_from("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg")
149                    .expect("Failed to create EventId.")
150            )
151            .expect("Failed to convert EventId to JSON."),
152            r#""$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg""#
153        );
154    }
155
156    #[test]
157    fn deserialize_valid_original_event_id() {
158        assert_eq!(
159            serde_json::from_str::<OwnedEventId>(r#""$39hvsi03hlne:example.com""#)
160                .expect("Failed to convert JSON to EventId"),
161            <&EventId>::try_from("$39hvsi03hlne:example.com").expect("Failed to create EventId.")
162        );
163    }
164
165    #[test]
166    fn deserialize_valid_base64_event_id() {
167        assert_eq!(
168            serde_json::from_str::<OwnedEventId>(
169                r#""$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk""#
170            )
171            .expect("Failed to convert JSON to EventId"),
172            <&EventId>::try_from("$acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk")
173                .expect("Failed to create EventId.")
174        );
175    }
176
177    #[test]
178    fn deserialize_valid_url_safe_base64_event_id() {
179        assert_eq!(
180            serde_json::from_str::<OwnedEventId>(
181                r#""$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg""#
182            )
183            .expect("Failed to convert JSON to EventId"),
184            <&EventId>::try_from("$Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg")
185                .expect("Failed to create EventId.")
186        );
187    }
188
189    #[test]
190    fn valid_original_event_id_with_explicit_standard_port() {
191        assert_eq!(
192            <&EventId>::try_from("$39hvsi03hlne:example.com:443")
193                .expect("Failed to create EventId."),
194            "$39hvsi03hlne:example.com:443"
195        );
196    }
197
198    #[test]
199    fn valid_original_event_id_with_non_standard_port() {
200        assert_eq!(
201            <&EventId>::try_from("$39hvsi03hlne:example.com:5000")
202                .expect("Failed to create EventId."),
203            "$39hvsi03hlne:example.com:5000"
204        );
205    }
206
207    #[test]
208    fn missing_original_event_id_sigil() {
209        assert_eq!(
210            <&EventId>::try_from("39hvsi03hlne:example.com").unwrap_err(),
211            IdParseError::MissingLeadingSigil
212        );
213    }
214
215    #[test]
216    fn missing_base64_event_id_sigil() {
217        assert_eq!(
218            <&EventId>::try_from("acR1l0raoZnm60CBwAVgqbZqoO/mYU81xysh1u7XcJk").unwrap_err(),
219            IdParseError::MissingLeadingSigil
220        );
221    }
222
223    #[test]
224    fn missing_url_safe_base64_event_id_sigil() {
225        assert_eq!(
226            <&EventId>::try_from("Rqnc-F-dvnEYJTyHq_iKxU2bZ1CI92-kuZq3a5lr5Zg").unwrap_err(),
227            IdParseError::MissingLeadingSigil
228        );
229    }
230
231    #[test]
232    fn invalid_event_id_host() {
233        assert_eq!(
234            <&EventId>::try_from("$39hvsi03hlne:/").unwrap_err(),
235            IdParseError::InvalidServerName
236        );
237    }
238
239    #[test]
240    fn invalid_event_id_port() {
241        assert_eq!(
242            <&EventId>::try_from("$39hvsi03hlne:example.com:notaport").unwrap_err(),
243            IdParseError::InvalidServerName
244        );
245    }
246}