diff --git a/crates/ruma-events/CHANGELOG.md b/crates/ruma-events/CHANGELOG.md index 6ce3ec0a..88883cfe 100644 --- a/crates/ruma-events/CHANGELOG.md +++ b/crates/ruma-events/CHANGELOG.md @@ -11,6 +11,7 @@ Breaking changes: Improvements: * Add `room::message::MessageType::body` accessor method +* Add (unstable) support for [MSC3083](https://github.com/matrix-org/matrix-doc/blob/main/proposals/3083-restricted-rooms.md) # 0.24.5 diff --git a/crates/ruma-events/src/room/join_rules.rs b/crates/ruma-events/src/room/join_rules.rs index 93415246..486a6508 100644 --- a/crates/ruma-events/src/room/join_rules.rs +++ b/crates/ruma-events/src/room/join_rules.rs @@ -1,8 +1,17 @@ //! Types for the *m.room.join_rules* event. +#[cfg(feature = "unstable-pre-spec")] +use std::collections::BTreeMap; + use ruma_events_macros::EventContent; +#[cfg(feature = "unstable-pre-spec")] +use ruma_identifiers::RoomId; use ruma_serde::StringEnum; +#[cfg(feature = "unstable-pre-spec")] +use serde::de::{DeserializeOwned, Deserializer, Error}; use serde::{Deserialize, Serialize}; +#[cfg(feature = "unstable-pre-spec")] +use serde_json::{value::RawValue as RawJsonValue, Value as JsonValue}; use crate::StateEvent; @@ -17,12 +26,29 @@ pub struct JoinRulesEventContent { /// The type of rules used for users wishing to join this room. #[ruma_event(skip_redaction)] pub join_rule: JoinRule, + + /// Allow rules used for the `restricted` join rule. + #[cfg(feature = "unstable-pre-spec")] + #[serde(default)] + #[ruma_event(skip_redaction)] + pub allow: Vec, } impl JoinRulesEventContent { /// Creates a new `JoinRulesEventContent` with the given rule. pub fn new(join_rule: JoinRule) -> Self { - Self { join_rule } + Self { + join_rule, + #[cfg(feature = "unstable-pre-spec")] + allow: Vec::new(), + } + } + + /// Creates a new `JoinRulesEventContent` with the restricted rule and the given set of allow + /// rules. + #[cfg(feature = "unstable-pre-spec")] + pub fn restricted(allow: Vec) -> Self { + Self { join_rule: JoinRule::Restricted, allow } } } @@ -44,6 +70,11 @@ pub enum JoinRule { /// Reserved but not yet implemented by the Matrix specification. Private, + /// Users can join the room if they are invited, or if they meet any of the conditions + /// described in a set of [`AllowRule`]s. + #[cfg(feature = "unstable-pre-spec")] + Restricted, + /// Anyone can join the room without any prior action. Public, @@ -57,3 +88,68 @@ impl JoinRule { self.as_ref() } } + +/// An allow rule which defines a condition that allows joining a room. +#[cfg(feature = "unstable-pre-spec")] +#[derive(Clone, Debug, PartialEq, Eq, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] +#[serde(tag = "type")] +pub enum AllowRule { + /// Joining is allowed if a user is already a member of the romm with the id `room_id`. + #[serde(rename = "m.room_membership")] + RoomMembership(RoomMembership), + + #[doc(hidden)] + _Custom(CustomAllowRule), +} + +/// Allow rule which grants permission to join based on the membership of another room. +#[cfg(feature = "unstable-pre-spec")] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] +pub struct RoomMembership { + /// The id of the room which being a member of grants permission to join another room. + pub room_id: RoomId, +} + +#[cfg(feature = "unstable-pre-spec")] +#[doc(hidden)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] +pub struct CustomAllowRule { + #[serde(rename = "type")] + rule_type: String, + #[serde(flatten)] + extra: BTreeMap, +} + +#[cfg(feature = "unstable-pre-spec")] +impl<'de> Deserialize<'de> for AllowRule { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + fn from_raw_json_value(raw: &RawJsonValue) -> Result { + serde_json::from_str(raw.get()).map_err(E::custom) + } + + let json: Box = Box::deserialize(deserializer)?; + + // Extracts the `type` value. + #[derive(Deserialize)] + struct ExtractType { + rule_type: Option, + } + + // Get the value of `type` if present. + let rule_type = serde_json::from_str::(json.get()) + .map_err(serde::de::Error::custom)? + .rule_type; + + match rule_type.as_deref() { + Some("m.room_membership") => from_raw_json_value(&json).map(Self::RoomMembership), + Some(_) => from_raw_json_value(&json).map(Self::_Custom), + None => Err(D::Error::missing_field("type")), + } + } +}