ruma_events/room/
join_rules.rs1pub use ruma_common::room::{AllowRule, JoinRule, Restricted};
6use ruma_common::{
7 room_version_rules::RedactionRules,
8 serde::{JsonCastable, JsonObject},
9};
10use ruma_macros::EventContent;
11use serde::{de, Deserialize, Serialize};
12
13use crate::{
14 EmptyStateKey, RedactContent, RedactedStateEventContent, StateEventContent, StateEventType,
15 StaticEventContent,
16};
17
18#[derive(Clone, Debug, Serialize, EventContent)]
22#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
23#[ruma_event(type = "m.room.join_rules", kind = State, state_key_type = EmptyStateKey, custom_redacted)]
24pub struct RoomJoinRulesEventContent {
25 #[ruma_event(skip_redaction)]
27 #[serde(flatten)]
28 pub join_rule: JoinRule,
29}
30
31impl RoomJoinRulesEventContent {
32 pub fn new(join_rule: JoinRule) -> Self {
34 Self { join_rule }
35 }
36
37 pub fn restricted(allow: Vec<AllowRule>) -> Self {
40 Self { join_rule: JoinRule::Restricted(Restricted::new(allow)) }
41 }
42
43 pub fn knock_restricted(allow: Vec<AllowRule>) -> Self {
46 Self { join_rule: JoinRule::KnockRestricted(Restricted::new(allow)) }
47 }
48}
49
50impl RedactContent for RoomJoinRulesEventContent {
51 type Redacted = RedactedRoomJoinRulesEventContent;
52
53 fn redact(self, _rules: &RedactionRules) -> Self::Redacted {
54 RedactedRoomJoinRulesEventContent { join_rule: self.join_rule }
55 }
56}
57
58impl<'de> Deserialize<'de> for RoomJoinRulesEventContent {
59 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
60 where
61 D: de::Deserializer<'de>,
62 {
63 let join_rule = JoinRule::deserialize(deserializer)?;
64 Ok(RoomJoinRulesEventContent { join_rule })
65 }
66}
67
68impl JsonCastable<RedactedRoomJoinRulesEventContent> for RoomJoinRulesEventContent {}
69
70#[derive(Clone, Debug, Serialize)]
72#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
73pub struct RedactedRoomJoinRulesEventContent {
74 #[serde(flatten)]
76 pub join_rule: JoinRule,
77}
78
79impl StaticEventContent for RedactedRoomJoinRulesEventContent {
80 const TYPE: &'static str = RoomJoinRulesEventContent::TYPE;
81 type IsPrefix = <RoomJoinRulesEventContent as StaticEventContent>::IsPrefix;
82}
83
84impl RedactedStateEventContent for RedactedRoomJoinRulesEventContent {
85 type StateKey = <RoomJoinRulesEventContent as StateEventContent>::StateKey;
86
87 fn event_type(&self) -> StateEventType {
88 StateEventType::RoomJoinRules
89 }
90}
91
92impl<'de> Deserialize<'de> for RedactedRoomJoinRulesEventContent {
93 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
94 where
95 D: de::Deserializer<'de>,
96 {
97 let join_rule = JoinRule::deserialize(deserializer)?;
98 Ok(Self { join_rule })
99 }
100}
101
102impl JsonCastable<JsonObject> for RedactedRoomJoinRulesEventContent {}
103
104impl RoomJoinRulesEvent {
105 pub fn join_rule(&self) -> &JoinRule {
107 match self {
108 Self::Original(ev) => &ev.content.join_rule,
109 Self::Redacted(ev) => &ev.content.join_rule,
110 }
111 }
112}
113
114impl SyncRoomJoinRulesEvent {
115 pub fn join_rule(&self) -> &JoinRule {
117 match self {
118 Self::Original(ev) => &ev.content.join_rule,
119 Self::Redacted(ev) => &ev.content.join_rule,
120 }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use assert_matches2::assert_matches;
127 use ruma_common::owned_room_id;
128
129 use super::{
130 AllowRule, JoinRule, OriginalSyncRoomJoinRulesEvent, RedactedRoomJoinRulesEventContent,
131 RoomJoinRulesEventContent,
132 };
133 use crate::room::join_rules::RedactedSyncRoomJoinRulesEvent;
134
135 #[test]
136 fn deserialize_content() {
137 let json = r#"{"join_rule": "public"}"#;
138
139 let event: RoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
140 assert_matches!(event, RoomJoinRulesEventContent { join_rule: JoinRule::Public });
141
142 let event: RedactedRoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
143 assert_matches!(event, RedactedRoomJoinRulesEventContent { join_rule: JoinRule::Public });
144 }
145
146 #[test]
147 fn deserialize_restricted() {
148 let json = r#"{
149 "join_rule": "restricted",
150 "allow": [
151 {
152 "type": "m.room_membership",
153 "room_id": "!mods:example.org"
154 },
155 {
156 "type": "m.room_membership",
157 "room_id": "!users:example.org"
158 }
159 ]
160 }"#;
161
162 let event: RoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
163 assert_matches!(event.join_rule, JoinRule::Restricted(restricted));
164 assert_eq!(
165 restricted.allow,
166 &[
167 AllowRule::room_membership(owned_room_id!("!mods:example.org")),
168 AllowRule::room_membership(owned_room_id!("!users:example.org"))
169 ]
170 );
171
172 let event: RedactedRoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
173 assert_matches!(event.join_rule, JoinRule::Restricted(restricted));
174 assert_eq!(
175 restricted.allow,
176 &[
177 AllowRule::room_membership(owned_room_id!("!mods:example.org")),
178 AllowRule::room_membership(owned_room_id!("!users:example.org"))
179 ]
180 );
181 }
182
183 #[test]
184 fn deserialize_restricted_event() {
185 let json = r#"{
186 "type": "m.room.join_rules",
187 "sender": "@admin:community.rs",
188 "content": {
189 "join_rule": "restricted",
190 "allow": [
191 { "type": "m.room_membership","room_id": "!KqeUnzmXPIhHRaWMTs:mccarty.io" }
192 ]
193 },
194 "state_key": "",
195 "origin_server_ts":1630508835342,
196 "unsigned": {
197 "age":4165521871
198 },
199 "event_id": "$0ACb9KSPlT3al3kikyRYvFhMqXPP9ZcQOBrsdIuh58U"
200 }"#;
201
202 assert_matches!(serde_json::from_str::<OriginalSyncRoomJoinRulesEvent>(json), Ok(_));
203 }
204
205 #[test]
206 fn deserialize_redacted_restricted_event() {
207 let json = r#"{
208 "type": "m.room.join_rules",
209 "sender": "@admin:community.rs",
210 "content": {
211 "join_rule": "restricted",
212 "allow": [
213 { "type": "m.room_membership","room_id": "!KqeUnzmXPIhHRaWMTs:mccarty.io" }
214 ]
215 },
216 "state_key": "",
217 "origin_server_ts":1630508835342,
218 "unsigned": {
219 "age":4165521871,
220 "redacted_because": {
221 "type": "m.room.redaction",
222 "content": {
223 "redacts": "$0ACb9KSPlT3al3kikyRYvFhMqXPP9ZcQOBrsdIuh58U"
224 },
225 "event_id": "$h29iv0s8",
226 "origin_server_ts": 1,
227 "sender": "@carl:example.com"
228 }
229 },
230 "event_id": "$0ACb9KSPlT3al3kikyRYvFhMqXPP9ZcQOBrsdIuh58U"
231 }"#;
232
233 assert_matches!(serde_json::from_str::<RedactedSyncRoomJoinRulesEvent>(json), Ok(_));
234 }
235
236 #[test]
237 fn restricted_room_no_allow_field() {
238 let json = r#"{"join_rule":"restricted"}"#;
239 let join_rules: RoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
240 assert_matches!(
241 join_rules,
242 RoomJoinRulesEventContent { join_rule: JoinRule::Restricted(_) }
243 );
244 }
245}