Add support for spaces
This commit is contained in:
parent
9d0cdb8c0e
commit
a322c8cf08
@ -2,6 +2,8 @@
|
||||
|
||||
use assign::assign;
|
||||
use ruma_api::ruma_api;
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
use ruma_events::room::create::RoomType;
|
||||
use ruma_events::{
|
||||
room::{
|
||||
create::{CreateEventContent, PreviousRoom},
|
||||
@ -129,27 +131,60 @@ pub struct CreationContent {
|
||||
/// A reference to the room this room replaces, if the previous room was upgraded.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub predecessor: Option<PreviousRoom>,
|
||||
|
||||
/// The room type.
|
||||
///
|
||||
/// This is currently only used for spaces.
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[serde(skip_serializing_if = "Option::is_none", rename = "type")]
|
||||
pub room_type: Option<RoomType>,
|
||||
}
|
||||
|
||||
impl CreationContent {
|
||||
/// Creates a new `CreationContent` with all fields defaulted.
|
||||
pub fn new() -> Self {
|
||||
Self { federate: true, predecessor: None }
|
||||
Self {
|
||||
federate: true,
|
||||
predecessor: None,
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
room_type: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a `CreationContent` and the other fields that a homeserver has to fill, construct
|
||||
/// a `CreateEventContent`.
|
||||
pub fn into_event_content(
|
||||
Self { federate, predecessor }: Self,
|
||||
self,
|
||||
creator: UserId,
|
||||
room_version: RoomVersionId,
|
||||
) -> CreateEventContent {
|
||||
assign!(CreateEventContent::new(creator), { federate, room_version, predecessor })
|
||||
#[allow(unused_mut)]
|
||||
let mut content = assign!(CreateEventContent::new(creator), {
|
||||
federate: self.federate,
|
||||
room_version: room_version,
|
||||
predecessor: self.predecessor,
|
||||
});
|
||||
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
{
|
||||
content.room_type = self.room_type;
|
||||
}
|
||||
|
||||
content
|
||||
}
|
||||
|
||||
/// Returns whether all fields have their default value.
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.federate && self.predecessor.is_none()
|
||||
let stable_fields = self.federate && self.predecessor.is_none();
|
||||
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
{
|
||||
stable_fields && self.room_type.is_none()
|
||||
}
|
||||
#[cfg(not(feature = "unstable-pre-spec"))]
|
||||
{
|
||||
stable_fields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -85,6 +85,12 @@ event_enum! {
|
||||
"m.room.third_party_invite",
|
||||
"m.room.tombstone",
|
||||
"m.room.topic",
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||
"m.space.child",
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||
"m.space.parent",
|
||||
}
|
||||
|
||||
/// Any to-device event.
|
||||
|
@ -176,6 +176,9 @@ pub mod relation;
|
||||
pub mod room;
|
||||
pub mod room_key;
|
||||
pub mod room_key_request;
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "unstable-pre-spec")))]
|
||||
pub mod space;
|
||||
pub mod sticker;
|
||||
pub mod tag;
|
||||
pub mod typing;
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
use ruma_events_macros::EventContent;
|
||||
use ruma_identifiers::{EventId, RoomId, RoomVersionId, UserId};
|
||||
use ruma_serde::StringEnum;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::StateEvent;
|
||||
@ -34,13 +35,38 @@ pub struct CreateEventContent {
|
||||
/// A reference to the room this room replaces, if the previous room was upgraded.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub predecessor: Option<PreviousRoom>,
|
||||
|
||||
/// The room type.
|
||||
///
|
||||
/// This is currently only used for spaces.
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[serde(skip_serializing_if = "Option::is_none", rename = "type")]
|
||||
pub room_type: Option<RoomType>,
|
||||
}
|
||||
|
||||
impl CreateEventContent {
|
||||
/// Creates a new `CreateEventContent` with the given creator.
|
||||
pub fn new(creator: UserId) -> Self {
|
||||
Self { creator, federate: true, room_version: default_room_version_id(), predecessor: None }
|
||||
Self {
|
||||
creator,
|
||||
federate: true,
|
||||
room_version: default_room_version_id(),
|
||||
predecessor: None,
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
room_type: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An enum of possible room types.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, StringEnum)]
|
||||
pub enum RoomType {
|
||||
/// Defines the room as a space.
|
||||
#[ruma_enum(rename = "m.space")]
|
||||
Space,
|
||||
/// Defines the room as a custom type.
|
||||
#[doc(hidden)]
|
||||
_Custom(String),
|
||||
}
|
||||
|
||||
/// A reference to an old room replaced during a room version upgrade.
|
||||
@ -75,6 +101,9 @@ mod tests {
|
||||
|
||||
use super::CreateEventContent;
|
||||
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
use super::RoomType;
|
||||
|
||||
#[test]
|
||||
fn serialization() {
|
||||
let content = CreateEventContent {
|
||||
@ -82,6 +111,8 @@ mod tests {
|
||||
federate: false,
|
||||
room_version: RoomVersionId::Version4,
|
||||
predecessor: None,
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
room_type: None,
|
||||
};
|
||||
|
||||
let json = json!({
|
||||
@ -93,6 +124,27 @@ mod tests {
|
||||
assert_eq!(to_json_value(&content).unwrap(), json);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[test]
|
||||
fn space_serialization() {
|
||||
let content = CreateEventContent {
|
||||
creator: user_id!("@carl:example.com"),
|
||||
federate: false,
|
||||
room_version: RoomVersionId::Version4,
|
||||
predecessor: None,
|
||||
room_type: Some(RoomType::Space),
|
||||
};
|
||||
|
||||
let json = json!({
|
||||
"creator": "@carl:example.com",
|
||||
"m.federate": false,
|
||||
"room_version": "4",
|
||||
"type": "m.space"
|
||||
});
|
||||
|
||||
assert_eq!(to_json_value(&content).unwrap(), json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn deserialization() {
|
||||
let json = json!({
|
||||
@ -111,7 +163,34 @@ mod tests {
|
||||
federate: true,
|
||||
room_version: RoomVersionId::Version4,
|
||||
predecessor: None,
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
room_type: None,
|
||||
} if creator == "@carl:example.com"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "unstable-pre-spec")]
|
||||
#[test]
|
||||
fn space_deserialization() {
|
||||
let json = json!({
|
||||
"creator": "@carl:example.com",
|
||||
"m.federate": true,
|
||||
"room_version": "4",
|
||||
"type": "m.space"
|
||||
});
|
||||
|
||||
assert_matches!(
|
||||
from_json_value::<Raw<CreateEventContent>>(json)
|
||||
.unwrap()
|
||||
.deserialize()
|
||||
.unwrap(),
|
||||
CreateEventContent {
|
||||
creator,
|
||||
federate: true,
|
||||
room_version: RoomVersionId::Version4,
|
||||
predecessor: None,
|
||||
room_type
|
||||
} if creator == "@carl:example.com" && room_type == Some(RoomType::Space)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
8
crates/ruma-events/src/space.rs
Normal file
8
crates/ruma-events/src/space.rs
Normal file
@ -0,0 +1,8 @@
|
||||
//! Types for the *m.space* events.
|
||||
//!
|
||||
//! See [MSC2758] and [MSC1772].
|
||||
//! [MSC2758]: https://github.com/matrix-org/matrix-doc/blob/master/proposals/2758-textual-id-grammar.md
|
||||
//! [MSC1772]: https://github.com/matrix-org/matrix-doc/blob/master/proposals/1772-groups-as-rooms.md
|
||||
|
||||
pub mod child;
|
||||
pub mod parent;
|
78
crates/ruma-events/src/space/child.rs
Normal file
78
crates/ruma-events/src/space/child.rs
Normal file
@ -0,0 +1,78 @@
|
||||
//! Types for the *m.space.child* event.
|
||||
|
||||
use ruma_events_macros::EventContent;
|
||||
use ruma_identifiers::ServerNameBox;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::StateEvent;
|
||||
|
||||
/// The admins of a space can advertise rooms and subspaces for their space by setting
|
||||
/// `m.space.child` state events.
|
||||
///
|
||||
/// The `state_key` is the ID of a child room or space, and the content must contain a `via` key
|
||||
/// which gives a list of candidate servers that can be used to join the room.
|
||||
pub type ChildEvent = StateEvent<ChildEventContent>;
|
||||
|
||||
/// The payload for `ChildEvent`.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.space.child", kind = State)]
|
||||
pub struct ChildEventContent {
|
||||
/// List of candidate servers that can be used to join the room.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub via: Option<Vec<ServerNameBox>>,
|
||||
|
||||
/// Provide a default ordering of siblings in the room list.
|
||||
///
|
||||
/// Rooms are sorted based on a lexicographic ordering of the Unicode codepoints of the
|
||||
/// characters in `order` values. Rooms with no `order` come last, in ascending numeric order
|
||||
/// of the origin_server_ts of their m.room.create events, or ascending lexicographic order of
|
||||
/// their room_ids in case of equal `origin_server_ts`. `order`s which are not strings, or do
|
||||
/// not consist solely of ascii characters in the range `\x20` (space) to `\x7E` (`~`), or
|
||||
/// consist of more than 50 characters, are forbidden and the field should be ignored if
|
||||
/// received.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub order: Option<String>,
|
||||
|
||||
/// Space admins can mark particular children of a space as "suggested".
|
||||
///
|
||||
/// This mainly serves as a hint to clients that that they can be displayed differently, for
|
||||
/// example by showing them eagerly in the room list. A child which is missing the `suggested`
|
||||
/// property is treated identically to a child with `"suggested": false`. A suggested child may
|
||||
/// be a room or a subspace.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub suggested: Option<bool>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ChildEventContent;
|
||||
use ruma_identifiers::server_name;
|
||||
use serde_json::{json, to_value as to_json_value};
|
||||
|
||||
#[test]
|
||||
fn space_child_serialization() {
|
||||
let content = ChildEventContent {
|
||||
via: Some(vec![server_name!("example.com")]),
|
||||
order: Some("uwu".to_owned()),
|
||||
suggested: Some(false),
|
||||
};
|
||||
|
||||
let json = json!({
|
||||
"via": ["example.com"],
|
||||
"order": "uwu",
|
||||
"suggested": false,
|
||||
});
|
||||
|
||||
assert_eq!(to_json_value(&content).unwrap(), json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn space_child_empty_serialization() {
|
||||
let content = ChildEventContent { via: None, order: None, suggested: None };
|
||||
|
||||
let json = json!({});
|
||||
|
||||
assert_eq!(to_json_value(&content).unwrap(), json);
|
||||
}
|
||||
}
|
64
crates/ruma-events/src/space/parent.rs
Normal file
64
crates/ruma-events/src/space/parent.rs
Normal file
@ -0,0 +1,64 @@
|
||||
//! Types for the *m.space.child* event.
|
||||
|
||||
use ruma_events_macros::EventContent;
|
||||
use ruma_identifiers::ServerNameBox;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::StateEvent;
|
||||
|
||||
/// Rooms can claim parents via the `m.space.parent` state event.
|
||||
///
|
||||
/// Similar to `m.space.child`, the `state_key` is the ID of the parent space, and the content must
|
||||
/// contain a `via` key which gives a list of candidate servers that can be used to join the
|
||||
/// parent.
|
||||
pub type ParentEvent = StateEvent<ParentEventContent>;
|
||||
|
||||
/// The payload for `ParentEvent`.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, EventContent)]
|
||||
#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)]
|
||||
#[ruma_event(type = "m.space.child", kind = State)]
|
||||
pub struct ParentEventContent {
|
||||
/// List of candidate servers that can be used to join the room.
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub via: Option<Vec<ServerNameBox>>,
|
||||
|
||||
/// Determines whether this is the main parent for the space.
|
||||
///
|
||||
/// When a user joins a room with a canonical parent, clients may switch to view the room in
|
||||
/// the context of that space, peeking into it in order to find other rooms and group them
|
||||
/// together. In practice, well behaved rooms should only have one `canonical` parent, but
|
||||
/// given this is not enforced: if multiple are present the client should select the one with
|
||||
/// the lowest room ID, as determined via a lexicographic ordering of the Unicode code-points.
|
||||
pub canonical: bool,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ParentEventContent;
|
||||
use ruma_identifiers::server_name;
|
||||
use serde_json::{json, to_value as to_json_value};
|
||||
|
||||
#[test]
|
||||
fn space_parent_serialization() {
|
||||
let content =
|
||||
ParentEventContent { via: Some(vec![server_name!("example.com")]), canonical: true };
|
||||
|
||||
let json = json!({
|
||||
"via": ["example.com"],
|
||||
"canonical": true,
|
||||
});
|
||||
|
||||
assert_eq!(to_json_value(&content).unwrap(), json);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn space_parent_empty_serialization() {
|
||||
let content = ParentEventContent { via: None, canonical: true };
|
||||
|
||||
let json = json!({
|
||||
"canonical": true,
|
||||
});
|
||||
|
||||
assert_eq!(to_json_value(&content).unwrap(), json);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user