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 self.federate && self.predecessor.is_none() && self.room_type.is_none()
193 }
194 }
195
196 impl Default for CreationContent {
197 fn default() -> Self {
198 Self::new()
199 }
200 }
201
202 #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
204 #[derive(Clone, StringEnum)]
205 #[ruma_enum(rename_all = "snake_case")]
206 #[non_exhaustive]
207 pub enum RoomPreset {
208 PrivateChat,
210
211 PublicChat,
213
214 TrustedPrivateChat,
217
218 #[doc(hidden)]
219 _Custom(PrivOwnedStr),
220 }
221}
222
223#[derive(Clone, Debug, Serialize, Default)]
234#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
235pub struct RoomPowerLevelsContentOverride {
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub ban: Option<Int>,
239
240 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
244 pub events: BTreeMap<TimelineEventType, Int>,
245
246 #[serde(skip_serializing_if = "Option::is_none")]
248 pub events_default: Option<Int>,
249
250 #[serde(skip_serializing_if = "Option::is_none")]
252 pub invite: Option<Int>,
253
254 #[serde(skip_serializing_if = "Option::is_none")]
256 pub kick: Option<Int>,
257
258 #[serde(skip_serializing_if = "Option::is_none")]
260 pub redact: Option<Int>,
261
262 #[serde(skip_serializing_if = "Option::is_none")]
264 pub state_default: Option<Int>,
265
266 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
270 pub users: BTreeMap<OwnedUserId, Int>,
271
272 #[serde(skip_serializing_if = "Option::is_none")]
274 pub users_default: Option<Int>,
275
276 #[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")]
280 pub notifications: NotificationPowerLevels,
281}
282
283impl RoomPowerLevelsContentOverride {
284 pub fn new() -> Self {
286 Self::default()
287 }
288}
289
290impl From<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {
291 fn from(value: RoomPowerLevelsEventContent) -> Self {
292 let RoomPowerLevelsEventContent {
293 ban,
294 events,
295 events_default,
296 invite,
297 kick,
298 redact,
299 state_default,
300 users,
301 users_default,
302 notifications,
303 ..
304 } = value;
305
306 Self {
307 ban: Some(ban),
308 events,
309 events_default: Some(events_default),
310 invite: Some(invite),
311 kick: Some(kick),
312 redact: Some(redact),
313 state_default: Some(state_default),
314 users,
315 users_default: Some(users_default),
316 notifications,
317 }
318 }
319}
320
321impl JsonCastable<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {}
322
323impl JsonCastable<RoomPowerLevelsContentOverride> for RoomPowerLevelsEventContent {}
324
325impl JsonCastable<JsonObject> for RoomPowerLevelsContentOverride {}
326
327#[cfg(test)]
328mod tests {
329 use std::collections::BTreeMap;
330
331 use assign::assign;
332 use js_int::int;
333 use maplit::btreemap;
334 use ruma_common::{
335 canonical_json::assert_to_canonical_json_eq, owned_user_id,
336 power_levels::NotificationPowerLevels,
337 };
338 use serde_json::json;
339
340 use super::RoomPowerLevelsContentOverride;
341
342 #[test]
343 fn serialization_of_power_levels_overridden_values_with_optional_fields_as_none() {
344 let power_levels = RoomPowerLevelsContentOverride {
345 ban: None,
346 events: BTreeMap::new(),
347 events_default: None,
348 invite: None,
349 kick: None,
350 redact: None,
351 state_default: None,
352 users: BTreeMap::new(),
353 users_default: None,
354 notifications: NotificationPowerLevels::default(),
355 };
356
357 assert_to_canonical_json_eq!(power_levels, json!({}));
358 }
359
360 #[test]
361 fn serialization_of_power_levels_overridden_values_with_all_fields() {
362 let user = owned_user_id!("@carl:example.com");
363 let power_levels_event = RoomPowerLevelsContentOverride {
364 ban: Some(int!(23)),
365 events: btreemap! {
366 "m.dummy".into() => int!(23)
367 },
368 events_default: Some(int!(23)),
369 invite: Some(int!(23)),
370 kick: Some(int!(23)),
371 redact: Some(int!(23)),
372 state_default: Some(int!(23)),
373 users: btreemap! {
374 user => int!(23)
375 },
376 users_default: Some(int!(23)),
377 notifications: assign!(NotificationPowerLevels::new(), { room: int!(23) }),
378 };
379
380 assert_to_canonical_json_eq!(
381 power_levels_event,
382 json!({
383 "ban": 23,
384 "events": {
385 "m.dummy": 23
386 },
387 "events_default": 23,
388 "invite": 23,
389 "kick": 23,
390 "redact": 23,
391 "state_default": 23,
392 "users": {
393 "@carl:example.com": 23
394 },
395 "users_default": 23,
396 "notifications": {
397 "room": 23
398 },
399 })
400 );
401 }
402}