1use std::collections::BTreeMap;
6
7use js_int::Int;
8use ruma_common::{
9 OwnedUserId,
10 power_levels::NotificationPowerLevels,
11 serde::{JsonCastable, JsonObject},
12};
13use ruma_events::{TimelineEventType, room::power_levels::RoomPowerLevelsEventContent};
14use serde::Serialize;
15
16pub mod v3 {
17 use assign::assign;
22 use ruma_common::{
23 OwnedRoomId, OwnedUserId, RoomVersionId,
24 api::{auth_scheme::AccessToken, request, response},
25 metadata,
26 room::RoomType,
27 serde::{Raw, StringEnum},
28 };
29 use ruma_events::{
30 AnyInitialStateEvent,
31 room::create::{PreviousRoom, RoomCreateEventContent},
32 };
33 use serde::{Deserialize, Serialize};
34
35 use super::RoomPowerLevelsContentOverride;
36 use crate::{PrivOwnedStr, membership::Invite3pid, room::Visibility};
37
38 metadata! {
39 method: POST,
40 rate_limited: false,
41 authentication: AccessToken,
42 history: {
43 1.0 => "/_matrix/client/r0/createRoom",
44 1.1 => "/_matrix/client/v3/createRoom",
45 }
46 }
47
48 #[request]
50 #[derive(Default)]
51 pub struct Request {
52 #[serde(default, skip_serializing_if = "Option::is_none")]
54 pub creation_content: Option<Raw<CreationContent>>,
55
56 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
60 pub initial_state: Vec<Raw<AnyInitialStateEvent>>,
61
62 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
66 pub invite: Vec<OwnedUserId>,
67
68 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
70 pub invite_3pid: Vec<Invite3pid>,
71
72 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
74 pub is_direct: bool,
75
76 #[serde(skip_serializing_if = "Option::is_none")]
79 pub name: Option<String>,
80
81 #[serde(skip_serializing_if = "Option::is_none")]
83 pub power_level_content_override: Option<Raw<RoomPowerLevelsContentOverride>>,
84
85 #[serde(skip_serializing_if = "Option::is_none")]
87 pub preset: Option<RoomPreset>,
88
89 #[serde(skip_serializing_if = "Option::is_none")]
91 pub room_alias_name: Option<String>,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
97 pub room_version: Option<RoomVersionId>,
98
99 #[serde(skip_serializing_if = "Option::is_none")]
102 pub topic: Option<String>,
103
104 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
109 pub visibility: Visibility,
110 }
111
112 #[response]
114 pub struct Response {
115 pub room_id: OwnedRoomId,
117 }
118
119 impl Request {
120 pub fn new() -> Self {
122 Default::default()
123 }
124 }
125
126 impl Response {
127 pub fn new(room_id: OwnedRoomId) -> Self {
129 Self { room_id }
130 }
131 }
132
133 #[derive(Clone, Debug, Deserialize, Serialize)]
138 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
139 pub struct CreationContent {
140 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
143 pub additional_creators: Vec<OwnedUserId>,
144
145 #[serde(
149 rename = "m.federate",
150 default = "ruma_common::serde::default_true",
151 skip_serializing_if = "ruma_common::serde::is_true"
152 )]
153 pub federate: bool,
154
155 #[serde(skip_serializing_if = "Option::is_none")]
157 pub predecessor: Option<PreviousRoom>,
158
159 #[serde(skip_serializing_if = "Option::is_none", rename = "type")]
161 pub room_type: Option<RoomType>,
162 }
163
164 impl CreationContent {
165 pub fn new() -> Self {
167 Self {
168 additional_creators: Vec::new(),
169 federate: true,
170 predecessor: None,
171 room_type: None,
172 }
173 }
174
175 pub fn into_event_content(
178 self,
179 creator: OwnedUserId,
180 room_version: RoomVersionId,
181 ) -> RoomCreateEventContent {
182 assign!(RoomCreateEventContent::new_v1(creator), {
183 federate: self.federate,
184 room_version: room_version,
185 predecessor: self.predecessor,
186 room_type: self.room_type
187 })
188 }
189
190 pub fn is_empty(&self) -> bool {
192 let Self { additional_creators, federate, predecessor, room_type } = self;
193 additional_creators.is_empty()
194 && *federate
195 && predecessor.is_none()
196 && room_type.is_none()
197 }
198 }
199
200 impl Default for CreationContent {
201 fn default() -> Self {
202 Self::new()
203 }
204 }
205
206 #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
208 #[derive(Clone, StringEnum)]
209 #[ruma_enum(rename_all = "snake_case")]
210 #[non_exhaustive]
211 pub enum RoomPreset {
212 PrivateChat,
214
215 PublicChat,
217
218 TrustedPrivateChat,
221
222 #[doc(hidden)]
223 _Custom(PrivOwnedStr),
224 }
225}
226
227#[derive(Clone, Debug, Serialize, Default)]
238#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
239pub struct RoomPowerLevelsContentOverride {
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub ban: Option<Int>,
243
244 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
248 pub events: BTreeMap<TimelineEventType, Int>,
249
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub events_default: Option<Int>,
253
254 #[serde(skip_serializing_if = "Option::is_none")]
256 pub invite: Option<Int>,
257
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub kick: Option<Int>,
261
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub redact: Option<Int>,
265
266 #[serde(skip_serializing_if = "Option::is_none")]
268 pub state_default: Option<Int>,
269
270 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
274 pub users: BTreeMap<OwnedUserId, Int>,
275
276 #[serde(skip_serializing_if = "Option::is_none")]
278 pub users_default: Option<Int>,
279
280 #[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")]
284 pub notifications: NotificationPowerLevels,
285}
286
287impl RoomPowerLevelsContentOverride {
288 pub fn new() -> Self {
290 Self::default()
291 }
292}
293
294impl From<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {
295 fn from(value: RoomPowerLevelsEventContent) -> Self {
296 let RoomPowerLevelsEventContent {
297 ban,
298 events,
299 events_default,
300 invite,
301 kick,
302 redact,
303 state_default,
304 users,
305 users_default,
306 notifications,
307 ..
308 } = value;
309
310 Self {
311 ban: Some(ban),
312 events,
313 events_default: Some(events_default),
314 invite: Some(invite),
315 kick: Some(kick),
316 redact: Some(redact),
317 state_default: Some(state_default),
318 users,
319 users_default: Some(users_default),
320 notifications,
321 }
322 }
323}
324
325impl JsonCastable<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {}
326
327impl JsonCastable<RoomPowerLevelsContentOverride> for RoomPowerLevelsEventContent {}
328
329impl JsonCastable<JsonObject> for RoomPowerLevelsContentOverride {}
330
331#[cfg(test)]
332mod tests {
333 use std::collections::BTreeMap;
334
335 use assign::assign;
336 use js_int::int;
337 use maplit::btreemap;
338 use ruma_common::{
339 canonical_json::assert_to_canonical_json_eq, owned_user_id,
340 power_levels::NotificationPowerLevels,
341 };
342 use serde_json::json;
343
344 use super::RoomPowerLevelsContentOverride;
345
346 #[test]
347 fn serialization_of_power_levels_overridden_values_with_optional_fields_as_none() {
348 let power_levels = RoomPowerLevelsContentOverride {
349 ban: None,
350 events: BTreeMap::new(),
351 events_default: None,
352 invite: None,
353 kick: None,
354 redact: None,
355 state_default: None,
356 users: BTreeMap::new(),
357 users_default: None,
358 notifications: NotificationPowerLevels::default(),
359 };
360
361 assert_to_canonical_json_eq!(power_levels, json!({}));
362 }
363
364 #[test]
365 fn serialization_of_power_levels_overridden_values_with_all_fields() {
366 let user = owned_user_id!("@carl:example.com");
367 let power_levels_event = RoomPowerLevelsContentOverride {
368 ban: Some(int!(23)),
369 events: btreemap! {
370 "m.dummy".into() => int!(23)
371 },
372 events_default: Some(int!(23)),
373 invite: Some(int!(23)),
374 kick: Some(int!(23)),
375 redact: Some(int!(23)),
376 state_default: Some(int!(23)),
377 users: btreemap! {
378 user => int!(23)
379 },
380 users_default: Some(int!(23)),
381 notifications: assign!(NotificationPowerLevels::new(), { room: int!(23) }),
382 };
383
384 assert_to_canonical_json_eq!(
385 power_levels_event,
386 json!({
387 "ban": 23,
388 "events": {
389 "m.dummy": 23
390 },
391 "events_default": 23,
392 "invite": 23,
393 "kick": 23,
394 "redact": 23,
395 "state_default": 23,
396 "users": {
397 "@carl:example.com": 23
398 },
399 "users_default": 23,
400 "notifications": {
401 "room": 23
402 },
403 })
404 );
405 }
406}