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(error = crate::Error)]
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(error = crate::Error)]
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")]
163 pub room_type: Option<RoomType>,
164 }
165
166 impl CreationContent {
167 pub fn new() -> Self {
169 Self {
170 additional_creators: Vec::new(),
171 federate: true,
172 predecessor: None,
173 room_type: None,
174 }
175 }
176
177 pub fn into_event_content(
180 self,
181 creator: OwnedUserId,
182 room_version: RoomVersionId,
183 ) -> RoomCreateEventContent {
184 assign!(RoomCreateEventContent::new_v1(creator), {
185 federate: self.federate,
186 room_version: room_version,
187 predecessor: self.predecessor,
188 room_type: self.room_type
189 })
190 }
191
192 pub fn is_empty(&self) -> bool {
194 self.federate && self.predecessor.is_none() && self.room_type.is_none()
195 }
196 }
197
198 impl Default for CreationContent {
199 fn default() -> Self {
200 Self::new()
201 }
202 }
203
204 #[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
206 #[derive(Clone, StringEnum)]
207 #[ruma_enum(rename_all = "snake_case")]
208 #[non_exhaustive]
209 pub enum RoomPreset {
210 PrivateChat,
212
213 PublicChat,
215
216 TrustedPrivateChat,
219
220 #[doc(hidden)]
221 _Custom(PrivOwnedStr),
222 }
223}
224
225#[derive(Clone, Debug, Serialize, Default)]
236#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
237pub struct RoomPowerLevelsContentOverride {
238 #[serde(skip_serializing_if = "Option::is_none")]
240 pub ban: Option<Int>,
241
242 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
246 pub events: BTreeMap<TimelineEventType, Int>,
247
248 #[serde(skip_serializing_if = "Option::is_none")]
250 pub events_default: Option<Int>,
251
252 #[serde(skip_serializing_if = "Option::is_none")]
254 pub invite: Option<Int>,
255
256 #[serde(skip_serializing_if = "Option::is_none")]
258 pub kick: Option<Int>,
259
260 #[serde(skip_serializing_if = "Option::is_none")]
262 pub redact: Option<Int>,
263
264 #[serde(skip_serializing_if = "Option::is_none")]
266 pub state_default: Option<Int>,
267
268 #[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
272 pub users: BTreeMap<OwnedUserId, Int>,
273
274 #[serde(skip_serializing_if = "Option::is_none")]
276 pub users_default: Option<Int>,
277
278 #[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")]
282 pub notifications: NotificationPowerLevels,
283}
284
285impl RoomPowerLevelsContentOverride {
286 pub fn new() -> Self {
288 Self::default()
289 }
290}
291
292impl From<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {
293 fn from(value: RoomPowerLevelsEventContent) -> Self {
294 let RoomPowerLevelsEventContent {
295 ban,
296 events,
297 events_default,
298 invite,
299 kick,
300 redact,
301 state_default,
302 users,
303 users_default,
304 notifications,
305 ..
306 } = value;
307
308 Self {
309 ban: Some(ban),
310 events,
311 events_default: Some(events_default),
312 invite: Some(invite),
313 kick: Some(kick),
314 redact: Some(redact),
315 state_default: Some(state_default),
316 users,
317 users_default: Some(users_default),
318 notifications,
319 }
320 }
321}
322
323impl JsonCastable<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {}
324
325impl JsonCastable<RoomPowerLevelsContentOverride> for RoomPowerLevelsEventContent {}
326
327impl JsonCastable<JsonObject> for RoomPowerLevelsContentOverride {}
328
329#[cfg(test)]
330mod tests {
331 use std::collections::BTreeMap;
332
333 use assign::assign;
334 use js_int::int;
335 use maplit::btreemap;
336 use ruma_common::{
337 canonical_json::assert_to_canonical_json_eq, power_levels::NotificationPowerLevels, user_id,
338 };
339 use serde_json::json;
340
341 use super::RoomPowerLevelsContentOverride;
342
343 #[test]
344 fn serialization_of_power_levels_overridden_values_with_optional_fields_as_none() {
345 let power_levels = RoomPowerLevelsContentOverride {
346 ban: None,
347 events: BTreeMap::new(),
348 events_default: None,
349 invite: None,
350 kick: None,
351 redact: None,
352 state_default: None,
353 users: BTreeMap::new(),
354 users_default: None,
355 notifications: NotificationPowerLevels::default(),
356 };
357
358 assert_to_canonical_json_eq!(power_levels, json!({}));
359 }
360
361 #[test]
362 fn serialization_of_power_levels_overridden_values_with_all_fields() {
363 let user = user_id!("@carl:example.com");
364 let power_levels_event = RoomPowerLevelsContentOverride {
365 ban: Some(int!(23)),
366 events: btreemap! {
367 "m.dummy".into() => int!(23)
368 },
369 events_default: Some(int!(23)),
370 invite: Some(int!(23)),
371 kick: Some(int!(23)),
372 redact: Some(int!(23)),
373 state_default: Some(int!(23)),
374 users: btreemap! {
375 user.to_owned() => int!(23)
376 },
377 users_default: Some(int!(23)),
378 notifications: assign!(NotificationPowerLevels::new(), { room: int!(23) }),
379 };
380
381 assert_to_canonical_json_eq!(
382 power_levels_event,
383 json!({
384 "ban": 23,
385 "events": {
386 "m.dummy": 23
387 },
388 "events_default": 23,
389 "invite": 23,
390 "kick": 23,
391 "redact": 23,
392 "state_default": 23,
393 "users": {
394 "@carl:example.com": 23
395 },
396 "users_default": 23,
397 "notifications": {
398 "room": 23
399 },
400 })
401 );
402 }
403}