Skip to main content

ruma_events/room/
create.rs

1//! Types for the [`m.room.create`] event.
2//!
3//! [`m.room.create`]: https://spec.matrix.org/v1.18/client-server-api/#mroomcreate
4
5use ruma_common::{
6    OwnedEventId, OwnedRoomId, OwnedUserId, RoomVersionId, room::RoomType,
7    room_version_rules::RedactionRules,
8};
9use ruma_macros::EventContent;
10use serde::{Deserialize, Serialize};
11
12use crate::{EmptyStateKey, RedactContent, RedactedStateEventContent, StateEventType};
13
14/// The content of an `m.room.create` event.
15///
16/// This is the first event in a room and cannot be changed.
17///
18/// It acts as the root of all other events.
19#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
20#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
21#[ruma_event(type = "m.room.create", kind = State, state_key_type = EmptyStateKey, custom_redacted)]
22pub struct RoomCreateEventContent {
23    /// The `user_id` of the room creator.
24    ///
25    /// This is set by the homeserver.
26    ///
27    /// This is required in room versions 1 trough 10, but is removed starting from room version
28    /// 11.
29    #[serde(skip_serializing_if = "Option::is_none")]
30    #[deprecated = "Since Matrix 1.8. This field was removed in Room version 11, clients should use the event's sender instead"]
31    pub creator: Option<OwnedUserId>,
32
33    /// Whether or not this room's data should be transferred to other homeservers.
34    #[serde(
35        rename = "m.federate",
36        default = "ruma_common::serde::default_true",
37        skip_serializing_if = "ruma_common::serde::is_true"
38    )]
39    pub federate: bool,
40
41    /// The version of the room.
42    ///
43    /// Defaults to `RoomVersionId::V1`.
44    #[serde(default = "default_room_version_id")]
45    pub room_version: RoomVersionId,
46
47    /// A reference to the room this room replaces, if the previous room was upgraded.
48    ///
49    /// With the `compat-lax-room-create-deser` cargo feature, this field is ignored if its
50    /// deserialization fails.
51    #[serde(skip_serializing_if = "Option::is_none")]
52    #[cfg_attr(
53        feature = "compat-lax-room-create-deser",
54        serde(default, deserialize_with = "ruma_common::serde::default_on_error")
55    )]
56    pub predecessor: Option<PreviousRoom>,
57
58    /// The room type.
59    #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
60    pub room_type: Option<RoomType>,
61
62    /// Additional room creators, considered to have "infinite" power level, in room version 12
63    /// onwards.
64    #[serde(skip_serializing_if = "Vec::is_empty", default)]
65    pub additional_creators: Vec<OwnedUserId>,
66}
67
68impl RoomCreateEventContent {
69    /// Creates a new `RoomCreateEventContent` with the given creator, as required for room versions
70    /// 1 through 10.
71    pub fn new_v1(creator: OwnedUserId) -> Self {
72        #[allow(deprecated)]
73        Self {
74            creator: Some(creator),
75            federate: true,
76            room_version: default_room_version_id(),
77            predecessor: None,
78            room_type: None,
79            additional_creators: Vec::new(),
80        }
81    }
82
83    /// Creates a new `RoomCreateEventContent` with the default values and no creator, as introduced
84    /// in room version 11.
85    ///
86    /// The room version is set to [`RoomVersionId::V11`].
87    pub fn new_v11() -> Self {
88        #[allow(deprecated)]
89        Self {
90            creator: None,
91            federate: true,
92            room_version: RoomVersionId::V11,
93            predecessor: None,
94            room_type: None,
95            additional_creators: Vec::new(),
96        }
97    }
98}
99
100impl RedactContent for RoomCreateEventContent {
101    type Redacted = RedactedRoomCreateEventContent;
102
103    fn redact(self, rules: &RedactionRules) -> Self::Redacted {
104        #[allow(deprecated)]
105        if rules.keep_room_create_content {
106            self
107        } else {
108            Self {
109                room_version: default_room_version_id(),
110                creator: self.creator,
111                ..Self::new_v11()
112            }
113        }
114    }
115}
116
117/// A reference to an old room replaced during a room version upgrade.
118#[derive(Clone, Debug, Deserialize, Serialize)]
119#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
120pub struct PreviousRoom {
121    /// The ID of the old room.
122    pub room_id: OwnedRoomId,
123
124    /// The event ID of the last known event in the old room.
125    #[deprecated = "\
126        This field should be sent by servers when possible for backwards compatibility \
127        but clients should not rely on it."]
128    #[serde(skip_serializing_if = "Option::is_none")]
129    pub event_id: Option<OwnedEventId>,
130}
131
132impl PreviousRoom {
133    /// Creates a new `PreviousRoom` from the given room ID.
134    pub fn new(room_id: OwnedRoomId) -> Self {
135        #[allow(deprecated)]
136        Self { room_id, event_id: None }
137    }
138}
139
140/// Used to default the `room_version` field to room version 1.
141fn default_room_version_id() -> RoomVersionId {
142    RoomVersionId::V1
143}
144
145/// Redacted form of [`RoomCreateEventContent`].
146///
147/// The redaction rules of this event changed with room version 11:
148///
149/// - In room versions 1 through 10, the `creator` field was preserved during redaction, starting
150///   from room version 11 the field is removed.
151/// - In room versions 1 through 10, all the other fields were redacted, starting from room version
152///   11 all the fields are preserved.
153pub type RedactedRoomCreateEventContent = RoomCreateEventContent;
154
155impl RedactedStateEventContent for RedactedRoomCreateEventContent {
156    type StateKey = EmptyStateKey;
157
158    fn event_type(&self) -> StateEventType {
159        StateEventType::RoomCreate
160    }
161}
162
163#[cfg(test)]
164mod tests {
165    use assert_matches2::assert_matches;
166    use ruma_common::{RoomVersionId, canonical_json::assert_to_canonical_json_eq, owned_user_id};
167    use serde_json::{from_value as from_json_value, json};
168
169    use super::{RoomCreateEventContent, RoomType};
170
171    #[test]
172    fn serialization() {
173        #[allow(deprecated)]
174        let content = RoomCreateEventContent {
175            creator: Some(owned_user_id!("@carl:example.com")),
176            federate: false,
177            room_version: RoomVersionId::V4,
178            predecessor: None,
179            room_type: None,
180            additional_creators: Vec::new(),
181        };
182
183        assert_to_canonical_json_eq!(
184            content,
185            json!({
186                "creator": "@carl:example.com",
187                "m.federate": false,
188                "room_version": "4",
189            }),
190        );
191    }
192
193    #[test]
194    fn space_serialization() {
195        #[allow(deprecated)]
196        let content = RoomCreateEventContent {
197            creator: Some(owned_user_id!("@carl:example.com")),
198            federate: false,
199            room_version: RoomVersionId::V4,
200            predecessor: None,
201            room_type: Some(RoomType::Space),
202            additional_creators: Vec::new(),
203        };
204
205        assert_to_canonical_json_eq!(
206            content,
207            json!({
208                "creator": "@carl:example.com",
209                "m.federate": false,
210                "room_version": "4",
211                "type": "m.space",
212            }),
213        );
214    }
215
216    #[test]
217    #[cfg(feature = "unstable-msc3417")]
218    fn call_serialization() {
219        #[allow(deprecated)]
220        let content = RoomCreateEventContent {
221            creator: Some(owned_user_id!("@carl:example.com")),
222            federate: false,
223            room_version: RoomVersionId::V4,
224            predecessor: None,
225            room_type: Some(RoomType::Call),
226            additional_creators: Vec::new(),
227        };
228
229        assert_to_canonical_json_eq!(
230            content,
231            json!({
232                "creator": "@carl:example.com",
233                "m.federate": false,
234                "room_version": "4",
235                "type": "org.matrix.msc3417.call",
236            }),
237        );
238    }
239
240    #[test]
241    #[allow(deprecated)]
242    fn deserialization() {
243        let json = json!({
244            "creator": "@carl:example.com",
245            "m.federate": true,
246            "room_version": "4"
247        });
248
249        let content = from_json_value::<RoomCreateEventContent>(json).unwrap();
250        assert_eq!(content.creator.unwrap(), "@carl:example.com");
251        assert!(content.federate);
252        assert_eq!(content.room_version, RoomVersionId::V4);
253        assert_matches!(content.predecessor, None);
254        assert_eq!(content.room_type, None);
255    }
256
257    #[test]
258    #[allow(deprecated)]
259    fn space_deserialization() {
260        let json = json!({
261            "creator": "@carl:example.com",
262            "m.federate": true,
263            "room_version": "4",
264            "type": "m.space"
265        });
266
267        let content = from_json_value::<RoomCreateEventContent>(json).unwrap();
268        assert_eq!(content.creator.unwrap(), "@carl:example.com");
269        assert!(content.federate);
270        assert_eq!(content.room_version, RoomVersionId::V4);
271        assert_matches!(content.predecessor, None);
272        assert_eq!(content.room_type, Some(RoomType::Space));
273    }
274
275    #[test]
276    #[cfg(feature = "unstable-msc3417")]
277    #[allow(deprecated)]
278    fn call_deserialization() {
279        let json = json!({
280            "creator": "@carl:example.com",
281            "m.federate": true,
282            "room_version": "4",
283            "type": "org.matrix.msc3417.call"
284        });
285
286        let content = from_json_value::<RoomCreateEventContent>(json).unwrap();
287        assert_eq!(content.creator.unwrap(), "@carl:example.com");
288        assert!(content.federate);
289        assert_eq!(content.room_version, RoomVersionId::V4);
290        assert_matches!(content.predecessor, None);
291        assert_eq!(content.room_type, Some(RoomType::Call));
292    }
293
294    #[test]
295    fn deserialize_valid_predecessor() {
296        let json = json!({
297            "m.federate": true,
298            "room_version": "11",
299            "predecessor": {
300                "room_id": "!room:localhost",
301                "event_id": "$eokpnkpn",
302            },
303        });
304
305        let content = from_json_value::<RoomCreateEventContent>(json).unwrap();
306        assert!(content.federate);
307        assert_eq!(content.room_version, RoomVersionId::V11);
308        assert_matches!(content.predecessor, Some(_));
309        assert_eq!(content.room_type, None);
310
311        let content = serde_json::from_str::<RoomCreateEventContent>(
312            r#"{"m.federate":true,"room_version":"11","predecessor":{"room_id":"!room:localhost","event_id":"$eokpnkpn"}}"#,
313        )
314        .unwrap();
315        assert!(content.federate);
316        assert_eq!(content.room_version, RoomVersionId::V11);
317        assert_matches!(content.predecessor, Some(_));
318        assert_eq!(content.room_type, None);
319    }
320
321    #[test]
322    #[cfg(feature = "compat-lax-room-create-deser")]
323    fn deserialize_invalid_predecessor() {
324        let json = json!({
325            "m.federate": true,
326            "room_version": "11",
327            "predecessor": "!room:localhost",
328        });
329
330        let content = from_json_value::<RoomCreateEventContent>(json).unwrap();
331        assert!(content.federate);
332        assert_eq!(content.room_version, RoomVersionId::V11);
333        assert_matches!(content.predecessor, None);
334        assert_eq!(content.room_type, None);
335
336        let content = serde_json::from_str::<RoomCreateEventContent>(
337            r#"{"m.federate":true,"room_version":"11","predecessor":"!room:localhost"}"#,
338        )
339        .unwrap();
340        assert!(content.federate);
341        assert_eq!(content.room_version, RoomVersionId::V11);
342        assert_matches!(content.predecessor, None);
343        assert_eq!(content.room_type, None);
344    }
345}