use std::collections::BTreeMap;
use js_int::Int;
use ruma_common::{
OwnedUserId,
power_levels::NotificationPowerLevels,
serde::{JsonCastable, JsonObject},
};
use ruma_events::{TimelineEventType, room::power_levels::RoomPowerLevelsEventContent};
use serde::Serialize;
pub mod v3 {
use assign::assign;
use ruma_common::{
OwnedRoomId, OwnedUserId, RoomVersionId,
api::{auth_scheme::AccessToken, request, response},
metadata,
room::RoomType,
serde::{Raw, StringEnum},
};
use ruma_events::{
AnyInitialStateEvent,
room::create::{PreviousRoom, RoomCreateEventContent},
};
use serde::{Deserialize, Serialize};
use super::RoomPowerLevelsContentOverride;
use crate::{PrivOwnedStr, membership::Invite3pid, room::Visibility};
metadata! {
method: POST,
rate_limited: false,
authentication: AccessToken,
history: {
1.0 => "/_matrix/client/r0/createRoom",
1.1 => "/_matrix/client/v3/createRoom",
}
}
#[request]
#[derive(Default)]
pub struct Request {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub creation_content: Option<Raw<CreationContent>>,
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub initial_state: Vec<Raw<AnyInitialStateEvent>>,
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub invite: Vec<OwnedUserId>,
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub invite_3pid: Vec<Invite3pid>,
#[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
pub is_direct: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub power_level_content_override: Option<Raw<RoomPowerLevelsContentOverride>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preset: Option<RoomPreset>,
#[serde(skip_serializing_if = "Option::is_none")]
pub room_alias_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub room_version: Option<RoomVersionId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub topic: Option<String>,
#[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
pub visibility: Visibility,
}
#[response]
pub struct Response {
pub room_id: OwnedRoomId,
}
impl Request {
pub fn new() -> Self {
Default::default()
}
}
impl Response {
pub fn new(room_id: OwnedRoomId) -> Self {
Self { room_id }
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
pub struct CreationContent {
#[serde(default, skip_serializing_if = "<[_]>::is_empty")]
pub additional_creators: Vec<OwnedUserId>,
#[serde(
rename = "m.federate",
default = "ruma_common::serde::default_true",
skip_serializing_if = "ruma_common::serde::is_true"
)]
pub federate: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub predecessor: Option<PreviousRoom>,
#[serde(skip_serializing_if = "Option::is_none", rename = "type")]
pub room_type: Option<RoomType>,
}
impl CreationContent {
pub fn new() -> Self {
Self {
additional_creators: Vec::new(),
federate: true,
predecessor: None,
room_type: None,
}
}
pub fn into_event_content(
self,
creator: OwnedUserId,
room_version: RoomVersionId,
) -> RoomCreateEventContent {
assign!(RoomCreateEventContent::new_v1(creator), {
federate: self.federate,
room_version: room_version,
predecessor: self.predecessor,
room_type: self.room_type
})
}
pub fn is_empty(&self) -> bool {
self.federate && self.predecessor.is_none() && self.room_type.is_none()
}
}
impl Default for CreationContent {
fn default() -> Self {
Self::new()
}
}
#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
#[derive(Clone, StringEnum)]
#[ruma_enum(rename_all = "snake_case")]
#[non_exhaustive]
pub enum RoomPreset {
PrivateChat,
PublicChat,
TrustedPrivateChat,
#[doc(hidden)]
_Custom(PrivOwnedStr),
}
}
#[derive(Clone, Debug, Serialize, Default)]
#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
pub struct RoomPowerLevelsContentOverride {
#[serde(skip_serializing_if = "Option::is_none")]
pub ban: Option<Int>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub events: BTreeMap<TimelineEventType, Int>,
#[serde(skip_serializing_if = "Option::is_none")]
pub events_default: Option<Int>,
#[serde(skip_serializing_if = "Option::is_none")]
pub invite: Option<Int>,
#[serde(skip_serializing_if = "Option::is_none")]
pub kick: Option<Int>,
#[serde(skip_serializing_if = "Option::is_none")]
pub redact: Option<Int>,
#[serde(skip_serializing_if = "Option::is_none")]
pub state_default: Option<Int>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub users: BTreeMap<OwnedUserId, Int>,
#[serde(skip_serializing_if = "Option::is_none")]
pub users_default: Option<Int>,
#[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")]
pub notifications: NotificationPowerLevels,
}
impl RoomPowerLevelsContentOverride {
pub fn new() -> Self {
Self::default()
}
}
impl From<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {
fn from(value: RoomPowerLevelsEventContent) -> Self {
let RoomPowerLevelsEventContent {
ban,
events,
events_default,
invite,
kick,
redact,
state_default,
users,
users_default,
notifications,
..
} = value;
Self {
ban: Some(ban),
events,
events_default: Some(events_default),
invite: Some(invite),
kick: Some(kick),
redact: Some(redact),
state_default: Some(state_default),
users,
users_default: Some(users_default),
notifications,
}
}
}
impl JsonCastable<RoomPowerLevelsEventContent> for RoomPowerLevelsContentOverride {}
impl JsonCastable<RoomPowerLevelsContentOverride> for RoomPowerLevelsEventContent {}
impl JsonCastable<JsonObject> for RoomPowerLevelsContentOverride {}
#[cfg(test)]
mod tests {
use std::collections::BTreeMap;
use assign::assign;
use js_int::int;
use maplit::btreemap;
use ruma_common::{
canonical_json::assert_to_canonical_json_eq, owned_user_id,
power_levels::NotificationPowerLevels,
};
use serde_json::json;
use super::RoomPowerLevelsContentOverride;
#[test]
fn serialization_of_power_levels_overridden_values_with_optional_fields_as_none() {
let power_levels = RoomPowerLevelsContentOverride {
ban: None,
events: BTreeMap::new(),
events_default: None,
invite: None,
kick: None,
redact: None,
state_default: None,
users: BTreeMap::new(),
users_default: None,
notifications: NotificationPowerLevels::default(),
};
assert_to_canonical_json_eq!(power_levels, json!({}));
}
#[test]
fn serialization_of_power_levels_overridden_values_with_all_fields() {
let user = owned_user_id!("@carl:example.com");
let power_levels_event = RoomPowerLevelsContentOverride {
ban: Some(int!(23)),
events: btreemap! {
"m.dummy".into() => int!(23)
},
events_default: Some(int!(23)),
invite: Some(int!(23)),
kick: Some(int!(23)),
redact: Some(int!(23)),
state_default: Some(int!(23)),
users: btreemap! {
user => int!(23)
},
users_default: Some(int!(23)),
notifications: assign!(NotificationPowerLevels::new(), { room: int!(23) }),
};
assert_to_canonical_json_eq!(
power_levels_event,
json!({
"ban": 23,
"events": {
"m.dummy": 23
},
"events_default": 23,
"invite": 23,
"kick": 23,
"redact": 23,
"state_default": 23,
"users": {
"@carl:example.com": 23
},
"users_default": 23,
"notifications": {
"room": 23
},
})
);
}
}