1use std::time::Duration;
6
7use ruma_common::OwnedDeviceId;
8use serde::{Deserialize, Serialize};
9
10pub mod cancel;
11pub mod subscribe;
12pub mod update;
13
14#[derive(Clone, Debug, PartialEq, Eq, Hash, Deserialize, Serialize)]
20#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
21pub struct StreamDescriptor {
22 pub device_id: OwnedDeviceId,
24
25 #[serde(
28 with = "ruma_common::serde::duration::opt_ms",
29 default,
30 skip_serializing_if = "Option::is_none"
31 )]
32 pub expiry_ms: Option<Duration>,
33}
34
35impl StreamDescriptor {
36 pub fn new(device_id: OwnedDeviceId) -> Self {
38 Self { device_id, expiry_ms: None }
39 }
40}
41
42#[cfg(test)]
43mod tests {
44 use std::time::Duration;
45
46 use ruma_common::{canonical_json::assert_to_canonical_json_eq, owned_device_id, serde::Raw};
47 use serde_json::json;
48
49 use super::StreamDescriptor;
50 use crate::room::message::{RoomMessageEventContent, RoomMessageEventContentWithoutRelation};
51
52 #[test]
53 fn descriptor_round_trips_inside_room_message() {
54 let mut content = RoomMessageEventContent::text_plain("Hello");
55 content.stream = Some(StreamDescriptor {
56 device_id: owned_device_id!("DEVICEID"),
57 expiry_ms: Some(Duration::from_millis(1_800_000)),
58 });
59
60 assert_to_canonical_json_eq!(
61 content,
62 json!({
63 "msgtype": "m.text",
64 "body": "Hello",
65 "org.matrix.msc4471.stream": {
66 "device_id": "DEVICEID",
67 "expiry_ms": 1_800_000,
68 },
69 })
70 );
71
72 let raw = Raw::new(&content).unwrap();
73 let deserialized: RoomMessageEventContent = raw.deserialize().unwrap();
74 let stream = deserialized.stream.unwrap();
75 assert_eq!(stream.device_id, "DEVICEID");
76 assert_eq!(stream.expiry_ms, Some(Duration::from_millis(1_800_000)));
77 }
78
79 #[test]
80 fn replacement_drops_stream() {
81 let mut content = RoomMessageEventContent::text_plain("Hello");
82 content.stream = Some(StreamDescriptor::new(owned_device_id!("DEVICEID")));
83
84 content.apply_replacement(RoomMessageEventContentWithoutRelation::text_plain("Done"));
85
86 assert!(content.stream.is_none());
87 assert_to_canonical_json_eq!(
88 content,
89 json!({
90 "msgtype": "m.text",
91 "body": "Done",
92 })
93 );
94 }
95
96 #[test]
97 fn replacement_keeps_stream() {
98 let mut content = RoomMessageEventContent::text_plain("Hello");
99 content.stream = Some(StreamDescriptor::new(owned_device_id!("DEVICEONE")));
100
101 let mut new_content = RoomMessageEventContentWithoutRelation::text_plain("world");
102 new_content.stream = Some(StreamDescriptor::new(owned_device_id!("DEVICETWO")));
103 content.apply_replacement(new_content);
104
105 let stream = content.stream.as_ref().unwrap();
106 assert_eq!(stream.device_id, "DEVICETWO");
107 assert_to_canonical_json_eq!(
108 content,
109 json!({
110 "msgtype": "m.text",
111 "body": "world",
112 "org.matrix.msc4471.stream": {
113 "device_id": "DEVICETWO",
114 },
115 })
116 );
117 }
118}