ruma_client_api/
space.rs

1//! Endpoints for spaces.
2//!
3//! See the [Matrix specification][spec] for more details about spaces.
4//!
5//! [spec]: https://spec.matrix.org/latest/client-server-api/#spaces
6
7use js_int::UInt;
8use ruma_common::{
9    room::RoomType, serde::Raw, space::SpaceRoomJoinRule, EventEncryptionAlgorithm, OwnedMxcUri,
10    OwnedRoomAliasId, OwnedRoomId, RoomVersionId,
11};
12use ruma_events::space::child::HierarchySpaceChildEvent;
13use serde::{Deserialize, Serialize};
14
15pub mod get_hierarchy;
16
17/// A chunk of a space hierarchy response, describing one room.
18///
19/// To create an instance of this type, first create a `SpaceHierarchyRoomsChunkInit` and convert it
20/// via `SpaceHierarchyRoomsChunk::from` / `.into()`.
21#[derive(Clone, Debug, Deserialize, Serialize)]
22#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
23pub struct SpaceHierarchyRoomsChunk {
24    /// The canonical alias of the room, if any.
25    #[serde(skip_serializing_if = "Option::is_none")]
26    #[cfg_attr(
27        feature = "compat-empty-string-null",
28        serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
29    )]
30    pub canonical_alias: Option<OwnedRoomAliasId>,
31
32    /// The name of the room, if any.
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub name: Option<String>,
35
36    /// The number of members joined to the room.
37    pub num_joined_members: UInt,
38
39    /// The ID of the room.
40    pub room_id: OwnedRoomId,
41
42    /// The topic of the room, if any.
43    #[serde(skip_serializing_if = "Option::is_none")]
44    pub topic: Option<String>,
45
46    /// Whether the room may be viewed by guest users without joining.
47    pub world_readable: bool,
48
49    /// Whether guest users may join the room and participate in it.
50    ///
51    /// If they can, they will be subject to ordinary power level rules like any other user.
52    pub guest_can_join: bool,
53
54    /// The URL for the room's avatar, if one is set.
55    ///
56    /// If you activate the `compat-empty-string-null` feature, this field being an empty string in
57    /// JSON will result in `None` here during deserialization.
58    #[serde(skip_serializing_if = "Option::is_none")]
59    #[cfg_attr(
60        feature = "compat-empty-string-null",
61        serde(default, deserialize_with = "ruma_common::serde::empty_string_as_none")
62    )]
63    pub avatar_url: Option<OwnedMxcUri>,
64
65    /// The join rule of the room.
66    #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
67    pub join_rule: SpaceRoomJoinRule,
68
69    /// If the room is a restricted room, these are the room IDs which are specified by the join
70    /// rules.
71    #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
72    pub allowed_room_ids: Vec<OwnedRoomId>,
73
74    /// The type of room from `m.room.create`, if any.
75    #[serde(skip_serializing_if = "Option::is_none")]
76    pub room_type: Option<RoomType>,
77
78    /// If the room is encrypted, the algorithm used for this room.
79    #[serde(skip_serializing_if = "Option::is_none")]
80    pub encryption: Option<EventEncryptionAlgorithm>,
81
82    /// The version of the room.
83    #[serde(skip_serializing_if = "Option::is_none")]
84    pub room_version: Option<RoomVersionId>,
85
86    /// The stripped `m.space.child` events of the space-room.
87    ///
88    /// If the room is not a space-room, this should be empty.
89    pub children_state: Vec<Raw<HierarchySpaceChildEvent>>,
90}
91
92/// Initial set of mandatory fields of `SpaceHierarchyRoomsChunk`.
93///
94/// This struct will not be updated even if additional fields are added to
95/// `SpaceHierarchyRoomsChunk` in a new (non-breaking) release of the Matrix specification.
96#[derive(Debug)]
97#[allow(clippy::exhaustive_structs)]
98pub struct SpaceHierarchyRoomsChunkInit {
99    /// The number of members joined to the room.
100    pub num_joined_members: UInt,
101
102    /// The ID of the room.
103    pub room_id: OwnedRoomId,
104
105    /// Whether the room may be viewed by guest users without joining.
106    pub world_readable: bool,
107
108    /// Whether guest users may join the room and participate in it.
109    ///
110    /// If they can, they will be subject to ordinary power level rules like any other user.
111    pub guest_can_join: bool,
112
113    /// The join rule of the room.
114    pub join_rule: SpaceRoomJoinRule,
115
116    /// The stripped `m.space.child` events of the space-room.
117    ///
118    /// If the room is not a space-room, this should be empty.
119    pub children_state: Vec<Raw<HierarchySpaceChildEvent>>,
120}
121
122impl From<SpaceHierarchyRoomsChunkInit> for SpaceHierarchyRoomsChunk {
123    fn from(init: SpaceHierarchyRoomsChunkInit) -> Self {
124        let SpaceHierarchyRoomsChunkInit {
125            num_joined_members,
126            room_id,
127            world_readable,
128            guest_can_join,
129            join_rule,
130            children_state,
131        } = init;
132
133        Self {
134            canonical_alias: None,
135            name: None,
136            num_joined_members,
137            room_id,
138            topic: None,
139            world_readable,
140            guest_can_join,
141            avatar_url: None,
142            join_rule,
143            allowed_room_ids: Vec::new(),
144            room_type: None,
145            encryption: None,
146            room_version: None,
147            children_state,
148        }
149    }
150}
151
152#[cfg(test)]
153mod tests {
154    use serde_json::{from_value as from_json_value, json};
155
156    use super::SpaceHierarchyRoomsChunk;
157
158    #[test]
159    fn deserialize_space_hierarchy_rooms_chunk() {
160        let json = json!({
161            "room_id": "!room:localhost",
162            "num_joined_members": 5,
163            "world_readable": false,
164            "guest_can_join": false,
165            "join_rule": "restricted",
166            "allowed_room_ids": ["!otherroom:localhost"],
167            "children_state": [
168                {
169                    "content": {
170                        "via": [
171                            "example.org"
172                        ]
173                    },
174                    "origin_server_ts": 1_629_413_349,
175                    "sender": "@alice:example.org",
176                    "state_key": "!a:example.org",
177                    "type": "m.space.child"
178                }
179            ],
180        });
181
182        let room = from_json_value::<SpaceHierarchyRoomsChunk>(json).unwrap();
183        assert_eq!(room.room_id, "!room:localhost");
184        let space_child = room.children_state[0].deserialize().unwrap();
185        assert_eq!(space_child.state_key, "!a:example.org");
186    }
187}