ruma_federation_api/
membership.rs

1//! Room membership endpoints.
2
3use ruma_common::serde::Raw;
4use ruma_events::AnyStrippedStateEvent;
5use serde::{Deserialize, Serialize};
6use serde_json::value::RawValue as RawJsonValue;
7
8pub mod create_invite;
9pub mod create_join_event;
10pub mod create_knock_event;
11pub mod create_leave_event;
12pub mod prepare_join_event;
13pub mod prepare_knock_event;
14pub mod prepare_leave_event;
15
16/// Possible event formats that may appear in stripped state.
17#[derive(Clone, Debug, Serialize)]
18#[serde(untagged)]
19#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
20pub enum RawStrippedState {
21    /// A stripped state event.
22    Stripped(Raw<AnyStrippedStateEvent>),
23
24    /// A full federation PDU.
25    #[cfg(feature = "unstable-msc4311")]
26    Pdu(Box<RawJsonValue>),
27}
28
29impl<'de> Deserialize<'de> for RawStrippedState {
30    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
31    where
32        D: ::serde::Deserializer<'de>,
33    {
34        let json = Box::<RawJsonValue>::deserialize(deserializer)?;
35
36        #[cfg(feature = "unstable-msc4311")]
37        {
38            use ruma_common::serde::from_raw_json_value;
39            use serde::de;
40
41            #[derive(Deserialize)]
42            struct PotentialPduDeHelper {
43                auth_events: Option<de::IgnoredAny>,
44                prev_events: Option<de::IgnoredAny>,
45                signatures: Option<de::IgnoredAny>,
46                hashes: Option<de::IgnoredAny>,
47            }
48
49            let PotentialPduDeHelper { auth_events, prev_events, signatures, hashes } =
50                from_raw_json_value(&json)?;
51
52            if auth_events.is_some()
53                && prev_events.is_some()
54                && signatures.is_some()
55                && hashes.is_some()
56            {
57                return Ok(Self::Pdu(json));
58            }
59        }
60
61        Ok(Raw::from_json(json).into())
62    }
63}
64
65impl From<Raw<AnyStrippedStateEvent>> for RawStrippedState {
66    fn from(value: Raw<AnyStrippedStateEvent>) -> Self {
67        Self::Stripped(value)
68    }
69}
70
71#[cfg(test)]
72mod tests {
73    use assert_matches2::assert_matches;
74    use ruma_common::{serde::Raw, user_id};
75    use ruma_events::{room::member::MembershipState, AnyStrippedStateEvent};
76    use serde_json::{from_value as from_json_value, json};
77
78    use super::RawStrippedState;
79
80    #[test]
81    fn deserialize_stripped_state() {
82        let user_id = user_id!("@patrick:localhost");
83        let content = json!({
84            "membership": "join",
85        });
86
87        // Stripped format.
88        let stripped_event_json = json!({
89            "content": content,
90            "sender": user_id,
91            "state_key": user_id,
92            "type": "m.room.member",
93        });
94        assert_matches!(
95            from_json_value::<RawStrippedState>(stripped_event_json).unwrap(),
96            RawStrippedState::Stripped(raw_stripped_event)
97        );
98        assert_matches!(
99            raw_stripped_event.deserialize().unwrap(),
100            AnyStrippedStateEvent::RoomMember(stripped_member_event)
101        );
102        assert_eq!(stripped_member_event.sender, user_id);
103        assert_eq!(stripped_member_event.state_key, user_id);
104        assert_eq!(stripped_member_event.content.membership, MembershipState::Join);
105
106        #[cfg(feature = "unstable-msc4311")]
107        {
108            // PDU format
109            let pdu_event_json = json!({
110                "auth_events": [
111                    "$one",
112                    "$two",
113                    "$three"
114                ],
115                "content": content,
116                "depth": 10,
117                "hashes": {
118                    "sha256": "thisisahash"
119                },
120                "origin_server_ts": 1_000_000,
121                "prev_events": [
122                    "$one",
123                    "$two",
124                    "$three"
125                ],
126                "room_id": "!room:localhost",
127                "sender": user_id,
128                "signatures": {
129                    "localhost": {
130                        "ed25519:1": "thisisakey"
131                    }
132                },
133                "state_key": user_id,
134                "type": "m.room.member",
135            });
136            assert_matches!(
137                from_json_value::<RawStrippedState>(pdu_event_json).unwrap(),
138                RawStrippedState::Pdu(_pdu_member_event)
139            );
140        }
141    }
142
143    #[test]
144    fn serialize_stripped_state() {
145        let user_id = user_id!("@patrick:localhost");
146        let content = json!({
147            "membership": "join",
148        });
149
150        // Stripped format.
151        let stripped_event_json = json!({
152            "content": content,
153            "sender": user_id,
154            "state_key": user_id,
155            "type": "m.room.member",
156        });
157        let raw_stripped_event =
158            Raw::new(&stripped_event_json).unwrap().cast_unchecked::<AnyStrippedStateEvent>();
159        let stripped_state = RawStrippedState::from(raw_stripped_event);
160
161        let stripped_event_json = serde_json::to_string(&stripped_state).unwrap();
162        assert_eq!(
163            stripped_event_json,
164            r#"{"content":{"membership":"join"},"sender":"@patrick:localhost","state_key":"@patrick:localhost","type":"m.room.member"}"#
165        );
166    }
167}