1use ruma_common::{
6 room::RoomType, room_version_rules::RedactionRules, OwnedEventId, OwnedRoomId, OwnedUserId,
7 RoomVersionId,
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")]
62 pub room_type: Option<RoomType>,
63
64 #[serde(skip_serializing_if = "Vec::is_empty", default)]
67 pub additional_creators: Vec<OwnedUserId>,
68}
69
70impl RoomCreateEventContent {
71 pub fn new_v1(creator: OwnedUserId) -> Self {
74 #[allow(deprecated)]
75 Self {
76 creator: Some(creator),
77 federate: true,
78 room_version: default_room_version_id(),
79 predecessor: None,
80 room_type: None,
81 additional_creators: Vec::new(),
82 }
83 }
84
85 pub fn new_v11() -> Self {
90 #[allow(deprecated)]
91 Self {
92 creator: None,
93 federate: true,
94 room_version: RoomVersionId::V11,
95 predecessor: None,
96 room_type: None,
97 additional_creators: Vec::new(),
98 }
99 }
100}
101
102impl RedactContent for RoomCreateEventContent {
103 type Redacted = RedactedRoomCreateEventContent;
104
105 fn redact(self, rules: &RedactionRules) -> Self::Redacted {
106 #[allow(deprecated)]
107 if rules.keep_room_create_content {
108 self
109 } else {
110 Self {
111 room_version: default_room_version_id(),
112 creator: self.creator,
113 ..Self::new_v11()
114 }
115 }
116 }
117}
118
119#[derive(Clone, Debug, Deserialize, Serialize)]
121#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
122pub struct PreviousRoom {
123 pub room_id: OwnedRoomId,
125
126 #[deprecated = "\
128 This field should be sent by servers when possible for backwards compatibility \
129 but clients should not rely on it."]
130 #[serde(skip_serializing_if = "Option::is_none")]
131 pub event_id: Option<OwnedEventId>,
132}
133
134impl PreviousRoom {
135 pub fn new(room_id: OwnedRoomId) -> Self {
137 #[allow(deprecated)]
138 Self { room_id, event_id: None }
139 }
140}
141
142fn default_room_version_id() -> RoomVersionId {
144 RoomVersionId::V1
145}
146
147pub type RedactedRoomCreateEventContent = RoomCreateEventContent;
156
157impl RedactedStateEventContent for RedactedRoomCreateEventContent {
158 type StateKey = EmptyStateKey;
159
160 fn event_type(&self) -> StateEventType {
161 StateEventType::RoomCreate
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use assert_matches2::assert_matches;
168 use ruma_common::{owned_user_id, RoomVersionId};
169 use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
170
171 use super::{RoomCreateEventContent, RoomType};
172
173 #[test]
174 fn serialization() {
175 #[allow(deprecated)]
176 let content = RoomCreateEventContent {
177 creator: Some(owned_user_id!("@carl:example.com")),
178 federate: false,
179 room_version: RoomVersionId::V4,
180 predecessor: None,
181 room_type: None,
182 additional_creators: Vec::new(),
183 };
184
185 let json = json!({
186 "creator": "@carl:example.com",
187 "m.federate": false,
188 "room_version": "4"
189 });
190
191 assert_eq!(to_json_value(&content).unwrap(), json);
192 }
193
194 #[test]
195 fn space_serialization() {
196 #[allow(deprecated)]
197 let content = RoomCreateEventContent {
198 creator: Some(owned_user_id!("@carl:example.com")),
199 federate: false,
200 room_version: RoomVersionId::V4,
201 predecessor: None,
202 room_type: Some(RoomType::Space),
203 additional_creators: Vec::new(),
204 };
205
206 let json = json!({
207 "creator": "@carl:example.com",
208 "m.federate": false,
209 "room_version": "4",
210 "type": "m.space"
211 });
212
213 assert_eq!(to_json_value(&content).unwrap(), json);
214 }
215
216 #[test]
217 #[allow(deprecated)]
218 fn deserialization() {
219 let json = json!({
220 "creator": "@carl:example.com",
221 "m.federate": true,
222 "room_version": "4"
223 });
224
225 let content = from_json_value::<RoomCreateEventContent>(json).unwrap();
226 assert_eq!(content.creator.unwrap(), "@carl:example.com");
227 assert!(content.federate);
228 assert_eq!(content.room_version, RoomVersionId::V4);
229 assert_matches!(content.predecessor, None);
230 assert_eq!(content.room_type, None);
231 }
232
233 #[test]
234 #[allow(deprecated)]
235 fn space_deserialization() {
236 let json = json!({
237 "creator": "@carl:example.com",
238 "m.federate": true,
239 "room_version": "4",
240 "type": "m.space"
241 });
242
243 let content = from_json_value::<RoomCreateEventContent>(json).unwrap();
244 assert_eq!(content.creator.unwrap(), "@carl:example.com");
245 assert!(content.federate);
246 assert_eq!(content.room_version, RoomVersionId::V4);
247 assert_matches!(content.predecessor, None);
248 assert_eq!(content.room_type, Some(RoomType::Space));
249 }
250
251 #[test]
252 fn deserialize_valid_predecessor() {
253 let json = json!({
254 "m.federate": true,
255 "room_version": "11",
256 "predecessor": {
257 "room_id": "!room:localhost",
258 "event_id": "$eokpnkpn",
259 },
260 });
261
262 let content = from_json_value::<RoomCreateEventContent>(json).unwrap();
263 assert!(content.federate);
264 assert_eq!(content.room_version, RoomVersionId::V11);
265 assert_matches!(content.predecessor, Some(_));
266 assert_eq!(content.room_type, None);
267
268 let content = serde_json::from_str::<RoomCreateEventContent>(
269 r#"{"m.federate":true,"room_version":"11","predecessor":{"room_id":"!room:localhost","event_id":"$eokpnkpn"}}"#,
270 )
271 .unwrap();
272 assert!(content.federate);
273 assert_eq!(content.room_version, RoomVersionId::V11);
274 assert_matches!(content.predecessor, Some(_));
275 assert_eq!(content.room_type, None);
276 }
277
278 #[test]
279 #[cfg(feature = "compat-lax-room-create-deser")]
280 fn deserialize_invalid_predecessor() {
281 let json = json!({
282 "m.federate": true,
283 "room_version": "11",
284 "predecessor": "!room:localhost",
285 });
286
287 let content = from_json_value::<RoomCreateEventContent>(json).unwrap();
288 assert!(content.federate);
289 assert_eq!(content.room_version, RoomVersionId::V11);
290 assert_matches!(content.predecessor, None);
291 assert_eq!(content.room_type, None);
292
293 let content = serde_json::from_str::<RoomCreateEventContent>(
294 r#"{"m.federate":true,"room_version":"11","predecessor":"!room:localhost"}"#,
295 )
296 .unwrap();
297 assert!(content.federate);
298 assert_eq!(content.room_version, RoomVersionId::V11);
299 assert_matches!(content.predecessor, None);
300 assert_eq!(content.room_type, None);
301 }
302}