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::{Deserialize, Serialize, de};
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)]
24#[serde(transparent)]
25pub struct RoomJoinRulesEventContent {
26 #[ruma_event(skip_redaction)]
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 From<RedactedRoomJoinRulesEventContent> for PossiblyRedactedRoomJoinRulesEventContent {
105 fn from(value: RedactedRoomJoinRulesEventContent) -> Self {
106 let RedactedRoomJoinRulesEventContent { join_rule } = value;
107 Self { join_rule }
108 }
109}
110
111impl RoomJoinRulesEvent {
112 pub fn join_rule(&self) -> &JoinRule {
114 match self {
115 Self::Original(ev) => &ev.content.join_rule,
116 Self::Redacted(ev) => &ev.content.join_rule,
117 }
118 }
119}
120
121impl SyncRoomJoinRulesEvent {
122 pub fn join_rule(&self) -> &JoinRule {
124 match self {
125 Self::Original(ev) => &ev.content.join_rule,
126 Self::Redacted(ev) => &ev.content.join_rule,
127 }
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use assert_matches2::assert_matches;
134 use ruma_common::owned_room_id;
135 use serde_json::json;
136
137 use super::{
138 AllowRule, JoinRule, OriginalSyncRoomJoinRulesEvent, RedactedRoomJoinRulesEventContent,
139 RoomJoinRulesEventContent,
140 };
141 use crate::room::join_rules::RedactedSyncRoomJoinRulesEvent;
142
143 #[test]
144 fn deserialize_content() {
145 let json = r#"{"join_rule": "public"}"#;
146
147 let event: RoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
148 assert_matches!(event, RoomJoinRulesEventContent { join_rule: JoinRule::Public });
149
150 let event: RedactedRoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
151 assert_matches!(event, RedactedRoomJoinRulesEventContent { join_rule: JoinRule::Public });
152 }
153
154 #[test]
155 fn deserialize_restricted() {
156 let json = r#"{
157 "join_rule": "restricted",
158 "allow": [
159 {
160 "type": "m.room_membership",
161 "room_id": "!mods:example.org"
162 },
163 {
164 "type": "m.room_membership",
165 "room_id": "!users:example.org"
166 }
167 ]
168 }"#;
169
170 let event: RoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
171 assert_matches!(event.join_rule, JoinRule::Restricted(restricted));
172 assert_eq!(
173 restricted.allow,
174 &[
175 AllowRule::room_membership(owned_room_id!("!mods:example.org")),
176 AllowRule::room_membership(owned_room_id!("!users:example.org"))
177 ]
178 );
179
180 let event: RedactedRoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
181 assert_matches!(event.join_rule, JoinRule::Restricted(restricted));
182 assert_eq!(
183 restricted.allow,
184 &[
185 AllowRule::room_membership(owned_room_id!("!mods:example.org")),
186 AllowRule::room_membership(owned_room_id!("!users:example.org"))
187 ]
188 );
189 }
190
191 #[test]
192 fn deserialize_restricted_event() {
193 let json = r#"{
194 "type": "m.room.join_rules",
195 "sender": "@admin:community.rs",
196 "content": {
197 "join_rule": "restricted",
198 "allow": [
199 { "type": "m.room_membership","room_id": "!KqeUnzmXPIhHRaWMTs:mccarty.io" }
200 ]
201 },
202 "state_key": "",
203 "origin_server_ts":1630508835342,
204 "unsigned": {
205 "age":4165521871
206 },
207 "event_id": "$0ACb9KSPlT3al3kikyRYvFhMqXPP9ZcQOBrsdIuh58U"
208 }"#;
209
210 assert_matches!(serde_json::from_str::<OriginalSyncRoomJoinRulesEvent>(json), Ok(_));
211 }
212
213 #[test]
214 fn deserialize_redacted_restricted_event() {
215 let json = r#"{
216 "type": "m.room.join_rules",
217 "sender": "@admin:community.rs",
218 "content": {
219 "join_rule": "restricted",
220 "allow": [
221 { "type": "m.room_membership","room_id": "!KqeUnzmXPIhHRaWMTs:mccarty.io" }
222 ]
223 },
224 "state_key": "",
225 "origin_server_ts":1630508835342,
226 "unsigned": {
227 "age":4165521871,
228 "redacted_because": {
229 "type": "m.room.redaction",
230 "content": {
231 "redacts": "$0ACb9KSPlT3al3kikyRYvFhMqXPP9ZcQOBrsdIuh58U"
232 },
233 "event_id": "$h29iv0s8",
234 "origin_server_ts": 1,
235 "sender": "@carl:example.com"
236 }
237 },
238 "event_id": "$0ACb9KSPlT3al3kikyRYvFhMqXPP9ZcQOBrsdIuh58U"
239 }"#;
240
241 assert_matches!(serde_json::from_str::<RedactedSyncRoomJoinRulesEvent>(json), Ok(_));
242 }
243
244 #[test]
245 fn restricted_room_no_allow_field() {
246 let json = r#"{"join_rule":"restricted"}"#;
247 let join_rules: RoomJoinRulesEventContent = serde_json::from_str(json).unwrap();
248 assert_matches!(
249 join_rules,
250 RoomJoinRulesEventContent { join_rule: JoinRule::Restricted(_) }
251 );
252 }
253
254 #[test]
255 fn reserialize_unsupported_join_rule() {
256 let json = json!({"join_rule": "local.matrix.custom", "foo": "bar"});
257
258 let content = serde_json::from_value::<RoomJoinRulesEventContent>(json.clone()).unwrap();
259 assert_eq!(content.join_rule.as_str(), "local.matrix.custom");
260 let data = content.join_rule.data();
261 assert_eq!(data.get("foo").unwrap().as_str(), Some("bar"));
262
263 assert_eq!(serde_json::to_value(&content).unwrap(), json);
264 }
265}