1use 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#[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 #[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 #[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 #[serde(default = "default_room_version_id")]
45 pub room_version: RoomVersionId,
46
47 #[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 #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
60 pub room_type: Option<RoomType>,
61
62 #[serde(skip_serializing_if = "Vec::is_empty", default)]
65 pub additional_creators: Vec<OwnedUserId>,
66}
67
68impl RoomCreateEventContent {
69 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 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#[derive(Clone, Debug, Deserialize, Serialize)]
119#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
120pub struct PreviousRoom {
121 pub room_id: OwnedRoomId,
123
124 #[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 pub fn new(room_id: OwnedRoomId) -> Self {
135 #[allow(deprecated)]
136 Self { room_id, event_id: None }
137 }
138}
139
140fn default_room_version_id() -> RoomVersionId {
142 RoomVersionId::V1
143}
144
145pub 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}