1use std::{cmp::Ordering, ops::Deref};
6
7use ruma_common::{
8 serde::{JsonCastable, JsonObject},
9 MilliSecondsSinceUnixEpoch, OwnedRoomId, OwnedServerName, OwnedSpaceChildOrder, OwnedUserId,
10 RoomId, SpaceChildOrder,
11};
12use ruma_macros::{Event, EventContent};
13use serde::{Deserialize, Serialize};
14
15use crate::{StateEvent, SyncStateEvent};
16
17#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
25#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
26#[ruma_event(type = "m.space.child", kind = State, state_key_type = OwnedRoomId)]
27pub struct SpaceChildEventContent {
28 pub via: Vec<OwnedServerName>,
30
31 #[serde(
43 default,
44 deserialize_with = "ruma_common::serde::default_on_error",
45 skip_serializing_if = "Option::is_none"
46 )]
47 pub order: Option<OwnedSpaceChildOrder>,
48
49 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
58 pub suggested: bool,
59}
60
61impl SpaceChildEventContent {
62 pub fn new(via: Vec<OwnedServerName>) -> Self {
64 Self { via, order: None, suggested: false }
65 }
66}
67
68#[derive(Clone, Debug, Event)]
71#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
72pub struct HierarchySpaceChildEvent {
73 pub content: SpaceChildEventContent,
75
76 pub sender: OwnedUserId,
78
79 pub state_key: OwnedRoomId,
81
82 pub origin_server_ts: MilliSecondsSinceUnixEpoch,
84}
85
86impl PartialEq for HierarchySpaceChildEvent {
87 fn eq(&self, other: &Self) -> bool {
88 self.space_child_ord_fields().eq(&other.space_child_ord_fields())
89 }
90}
91
92impl Eq for HierarchySpaceChildEvent {}
93
94impl Ord for HierarchySpaceChildEvent {
95 fn cmp(&self, other: &Self) -> Ordering {
96 self.space_child_ord_fields().cmp(&other.space_child_ord_fields())
97 }
98}
99
100impl PartialOrd for HierarchySpaceChildEvent {
101 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
102 Some(self.cmp(other))
103 }
104}
105
106impl JsonCastable<HierarchySpaceChildEvent> for SpaceChildEvent {}
107
108impl JsonCastable<HierarchySpaceChildEvent> for OriginalSpaceChildEvent {}
109
110impl JsonCastable<HierarchySpaceChildEvent> for SyncSpaceChildEvent {}
111
112impl JsonCastable<HierarchySpaceChildEvent> for OriginalSyncSpaceChildEvent {}
113
114impl JsonCastable<JsonObject> for HierarchySpaceChildEvent {}
115
116pub trait SpaceChildOrd {
124 #[doc(hidden)]
125 fn space_child_ord_fields(&self) -> SpaceChildOrdFields<'_>;
126
127 fn cmp_space_child(&self, other: &impl SpaceChildOrd) -> Ordering {
132 self.space_child_ord_fields().cmp(&other.space_child_ord_fields())
133 }
134}
135
136#[doc(hidden)]
141#[derive(PartialEq, Eq)]
142pub struct SpaceChildOrdFields<'a> {
143 order: Option<&'a SpaceChildOrder>,
144 origin_server_ts: MilliSecondsSinceUnixEpoch,
145 state_key: &'a RoomId,
146}
147
148impl<'a> SpaceChildOrdFields<'a> {
149 fn new(
153 order: Option<&'a SpaceChildOrder>,
154 origin_server_ts: MilliSecondsSinceUnixEpoch,
155 state_key: &'a RoomId,
156 ) -> Self {
157 Self { order, origin_server_ts, state_key }
158 }
159}
160
161impl<'a> Ord for SpaceChildOrdFields<'a> {
162 fn cmp(&self, other: &Self) -> Ordering {
163 match (self.order, other.order) {
164 (Some(_), None) => Ordering::Less,
166 (None, Some(_)) => Ordering::Greater,
167 (Some(self_order), Some(other_order)) => self_order.cmp(other_order),
168 (None, None) => Ordering::Equal,
169 }
170 .then_with(|| self.origin_server_ts.cmp(&other.origin_server_ts))
171 .then_with(|| self.state_key.cmp(other.state_key))
172 }
173}
174
175impl<'a> PartialOrd for SpaceChildOrdFields<'a> {
176 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
177 Some(self.cmp(other))
178 }
179}
180
181impl<T> SpaceChildOrd for &T
182where
183 T: SpaceChildOrd,
184{
185 fn space_child_ord_fields(&self) -> SpaceChildOrdFields<'_> {
186 (*self).space_child_ord_fields()
187 }
188}
189
190impl SpaceChildOrd for OriginalSpaceChildEvent {
191 fn space_child_ord_fields(&self) -> SpaceChildOrdFields<'_> {
192 SpaceChildOrdFields::new(
193 self.content.order.as_deref(),
194 self.origin_server_ts,
195 &self.state_key,
196 )
197 }
198}
199
200impl SpaceChildOrd for RedactedSpaceChildEvent {
201 fn space_child_ord_fields(&self) -> SpaceChildOrdFields<'_> {
202 SpaceChildOrdFields::new(None, self.origin_server_ts, &self.state_key)
203 }
204}
205
206impl SpaceChildOrd for SpaceChildEvent {
207 fn space_child_ord_fields(&self) -> SpaceChildOrdFields<'_> {
208 match self {
209 StateEvent::Original(original) => original.space_child_ord_fields(),
210 StateEvent::Redacted(redacted) => redacted.space_child_ord_fields(),
211 }
212 }
213}
214
215impl SpaceChildOrd for OriginalSyncSpaceChildEvent {
216 fn space_child_ord_fields(&self) -> SpaceChildOrdFields<'_> {
217 SpaceChildOrdFields::new(
218 self.content.order.as_deref(),
219 self.origin_server_ts,
220 &self.state_key,
221 )
222 }
223}
224
225impl SpaceChildOrd for RedactedSyncSpaceChildEvent {
226 fn space_child_ord_fields(&self) -> SpaceChildOrdFields<'_> {
227 SpaceChildOrdFields::new(None, self.origin_server_ts, &self.state_key)
228 }
229}
230
231impl SpaceChildOrd for SyncSpaceChildEvent {
232 fn space_child_ord_fields(&self) -> SpaceChildOrdFields<'_> {
233 match self {
234 SyncStateEvent::Original(original) => original.space_child_ord_fields(),
235 SyncStateEvent::Redacted(redacted) => redacted.space_child_ord_fields(),
236 }
237 }
238}
239
240impl SpaceChildOrd for HierarchySpaceChildEvent {
241 fn space_child_ord_fields(&self) -> SpaceChildOrdFields<'_> {
242 SpaceChildOrdFields::new(
243 self.content.order.as_deref(),
244 self.origin_server_ts,
245 &self.state_key,
246 )
247 }
248}
249
250#[derive(Debug, Clone)]
257#[allow(clippy::exhaustive_structs)]
258pub struct SpaceChildOrdHelper<T: SpaceChildOrd>(pub T);
259
260impl<T: SpaceChildOrd> PartialEq for SpaceChildOrdHelper<T> {
261 fn eq(&self, other: &Self) -> bool {
262 self.0.space_child_ord_fields().eq(&other.0.space_child_ord_fields())
263 }
264}
265
266impl<T: SpaceChildOrd> Eq for SpaceChildOrdHelper<T> {}
267
268impl<T: SpaceChildOrd> Ord for SpaceChildOrdHelper<T> {
269 fn cmp(&self, other: &Self) -> Ordering {
270 self.0.cmp_space_child(&other.0)
271 }
272}
273
274impl<T: SpaceChildOrd> PartialOrd for SpaceChildOrdHelper<T> {
275 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
276 Some(self.cmp(other))
277 }
278}
279
280impl<T: SpaceChildOrd> Deref for SpaceChildOrdHelper<T> {
281 type Target = T;
282
283 fn deref(&self) -> &Self::Target {
284 &self.0
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use std::{collections::BTreeSet, iter::repeat_n};
291
292 use js_int::{uint, UInt};
293 use ruma_common::{
294 owned_server_name, owned_user_id, room_id, server_name, MilliSecondsSinceUnixEpoch, RoomId,
295 SpaceChildOrder,
296 };
297 use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
298
299 use super::{
300 HierarchySpaceChildEvent, SpaceChildEventContent, SpaceChildOrd, SpaceChildOrdHelper,
301 };
302
303 #[test]
304 fn space_child_serialization() {
305 let content = SpaceChildEventContent {
306 via: vec![server_name!("example.com").to_owned()],
307 order: Some(SpaceChildOrder::parse("uwu").unwrap()),
308 suggested: false,
309 };
310
311 let json = json!({
312 "via": ["example.com"],
313 "order": "uwu",
314 });
315
316 assert_eq!(to_json_value(&content).unwrap(), json);
317 }
318
319 #[test]
320 fn space_child_empty_serialization() {
321 let content = SpaceChildEventContent { via: vec![], order: None, suggested: false };
322
323 let json = json!({ "via": [] });
324
325 assert_eq!(to_json_value(&content).unwrap(), json);
326 }
327
328 #[test]
329 fn space_child_content_deserialization_order() {
330 let via = server_name!("localhost");
331
332 let json = json!({
334 "order": "aaa",
335 "via": [via],
336 });
337 let content = from_json_value::<SpaceChildEventContent>(json).unwrap();
338 assert_eq!(content.order.unwrap(), "aaa");
339 assert!(!content.suggested);
340 assert_eq!(content.via, &[via]);
341
342 let json = json!({
344 "order": 2,
345 "via": [via],
346 });
347 let content = from_json_value::<SpaceChildEventContent>(json).unwrap();
348 assert_eq!(content.order, None);
349 assert!(!content.suggested);
350 assert_eq!(content.via, &[via]);
351
352 let json = json!({
354 "order": "",
355 "via": [via],
356 });
357 let content = from_json_value::<SpaceChildEventContent>(json).unwrap();
358 assert_eq!(content.order.unwrap(), "");
359 assert!(!content.suggested);
360 assert_eq!(content.via, &[via]);
361
362 let order = repeat_n('a', 60).collect::<String>();
364 let json = json!({
365 "order": order,
366 "via": [via],
367 });
368 let content = from_json_value::<SpaceChildEventContent>(json).unwrap();
369 assert_eq!(content.order, None);
370 assert!(!content.suggested);
371 assert_eq!(content.via, &[via]);
372
373 let json = json!({
375 "order": "🔝",
376 "via": [via],
377 });
378 let content = from_json_value::<SpaceChildEventContent>(json).unwrap();
379 assert_eq!(content.order, None);
380 assert!(!content.suggested);
381 assert_eq!(content.via, &[via]);
382 }
383
384 #[test]
385 fn hierarchy_space_child_deserialization() {
386 let json = json!({
387 "content": {
388 "via": [
389 "example.org"
390 ]
391 },
392 "origin_server_ts": 1_629_413_349,
393 "sender": "@alice:example.org",
394 "state_key": "!a:example.org",
395 "type": "m.space.child"
396 });
397
398 let ev = from_json_value::<HierarchySpaceChildEvent>(json).unwrap();
399 assert_eq!(ev.origin_server_ts, MilliSecondsSinceUnixEpoch(uint!(1_629_413_349)));
400 assert_eq!(ev.sender, "@alice:example.org");
401 assert_eq!(ev.state_key, "!a:example.org");
402 assert_eq!(ev.content.via, ["example.org"]);
403 assert_eq!(ev.content.order, None);
404 assert!(!ev.content.suggested);
405 }
406
407 fn hierarchy_space_child_event(
409 state_key: &RoomId,
410 order: Option<&str>,
411 origin_server_ts: UInt,
412 ) -> HierarchySpaceChildEvent {
413 let mut content = SpaceChildEventContent::new(vec![owned_server_name!("example.org")]);
414 content.order = order.and_then(|order| SpaceChildOrder::parse(order).ok());
415
416 HierarchySpaceChildEvent {
417 content,
418 sender: owned_user_id!("@alice:example.org"),
419 state_key: state_key.to_owned(),
420 origin_server_ts: MilliSecondsSinceUnixEpoch(origin_server_ts),
421 }
422 }
423
424 #[test]
425 fn space_child_ord_spec_example() {
426 let child_a = hierarchy_space_child_event(
428 room_id!("!a:example.org"),
429 Some("aaaa"),
430 uint!(1_640_141_000),
431 );
432 let child_b = hierarchy_space_child_event(
433 room_id!("!b:example.org"),
434 Some(" "),
435 uint!(1_640_341_000),
436 );
437 let child_c = hierarchy_space_child_event(
438 room_id!("!c:example.org"),
439 Some("first"),
440 uint!(1_640_841_000),
441 );
442 let child_d =
443 hierarchy_space_child_event(room_id!("!d:example.org"), None, uint!(1_640_741_000));
444 let child_e =
445 hierarchy_space_child_event(room_id!("!e:example.org"), None, uint!(1_640_641_000));
446
447 let events =
448 [child_a.clone(), child_b.clone(), child_c.clone(), child_d.clone(), child_e.clone()];
449
450 let mut sorted_events = events.clone();
452 sorted_events.sort_by(SpaceChildOrd::cmp_space_child);
453 assert_eq!(sorted_events[0].state_key, child_b.state_key);
454 assert_eq!(sorted_events[1].state_key, child_a.state_key);
455 assert_eq!(sorted_events[2].state_key, child_c.state_key);
456 assert_eq!(sorted_events[3].state_key, child_e.state_key);
457 assert_eq!(sorted_events[4].state_key, child_d.state_key);
458
459 let sorted_events = events.clone().into_iter().collect::<BTreeSet<_>>();
461 let mut iter = sorted_events.iter();
462 assert_eq!(iter.next().unwrap().state_key, child_b.state_key);
463 assert_eq!(iter.next().unwrap().state_key, child_a.state_key);
464 assert_eq!(iter.next().unwrap().state_key, child_c.state_key);
465 assert_eq!(iter.next().unwrap().state_key, child_e.state_key);
466 assert_eq!(iter.next().unwrap().state_key, child_d.state_key);
467
468 let sorted_events = events.into_iter().map(SpaceChildOrdHelper).collect::<BTreeSet<_>>();
470 let mut iter = sorted_events.iter();
471 assert_eq!(iter.next().unwrap().state_key, child_b.state_key);
472 assert_eq!(iter.next().unwrap().state_key, child_a.state_key);
473 assert_eq!(iter.next().unwrap().state_key, child_c.state_key);
474 assert_eq!(iter.next().unwrap().state_key, child_e.state_key);
475 assert_eq!(iter.next().unwrap().state_key, child_d.state_key);
476 }
477
478 #[test]
479 fn space_child_ord_other_example() {
480 let child_a = hierarchy_space_child_event(
482 room_id!("!a:example.org"),
483 Some("🔝"),
484 uint!(1_640_141_000),
485 );
486 let child_b = hierarchy_space_child_event(
487 room_id!("!b:example.org"),
488 Some(" "),
489 uint!(1_640_341_000),
490 );
491 let child_c =
492 hierarchy_space_child_event(room_id!("!c:example.org"), None, uint!(1_640_841_000));
493 let child_d =
494 hierarchy_space_child_event(room_id!("!d:example.org"), None, uint!(1_640_741_000));
495 let child_e =
496 hierarchy_space_child_event(room_id!("!e:example.org"), None, uint!(1_640_741_000));
497
498 let mut events =
499 [child_a.clone(), child_b.clone(), child_c.clone(), child_d.clone(), child_e.clone()];
500
501 events.sort_by(SpaceChildOrd::cmp_space_child);
502
503 let mut sorted_events = events.clone();
505 sorted_events.sort_by(SpaceChildOrd::cmp_space_child);
506 assert_eq!(sorted_events[0].state_key, child_b.state_key);
507 assert_eq!(sorted_events[1].state_key, child_a.state_key);
508 assert_eq!(sorted_events[2].state_key, child_d.state_key);
509 assert_eq!(sorted_events[3].state_key, child_e.state_key);
510 assert_eq!(sorted_events[4].state_key, child_c.state_key);
511
512 let sorted_events = events.clone().into_iter().collect::<BTreeSet<_>>();
514 let mut iter = sorted_events.iter();
515 assert_eq!(iter.next().unwrap().state_key, child_b.state_key);
516 assert_eq!(iter.next().unwrap().state_key, child_a.state_key);
517 assert_eq!(iter.next().unwrap().state_key, child_d.state_key);
518 assert_eq!(iter.next().unwrap().state_key, child_e.state_key);
519 assert_eq!(iter.next().unwrap().state_key, child_c.state_key);
520
521 let sorted_events = events.into_iter().map(SpaceChildOrdHelper).collect::<BTreeSet<_>>();
523 let mut iter = sorted_events.iter();
524 assert_eq!(iter.next().unwrap().state_key, child_b.state_key);
525 assert_eq!(iter.next().unwrap().state_key, child_a.state_key);
526 assert_eq!(iter.next().unwrap().state_key, child_d.state_key);
527 assert_eq!(iter.next().unwrap().state_key, child_e.state_key);
528 assert_eq!(iter.next().unwrap().state_key, child_c.state_key);
529 }
530}