ruma_federation_api/serde/
v1_pdu.rs

1//! A module to deserialize a response from incorrectly specified endpoint:
2//!
3//! - [PUT /_matrix/federation/v1/send_join/{roomId}/{eventId}](https://spec.matrix.org/latest/server-server-api/#put_matrixfederationv1send_joinroomideventid)
4//! - [PUT /_matrix/federation/v1/invite/{roomId}/{eventId}](https://spec.matrix.org/latest/server-server-api/#put_matrixfederationv1inviteroomideventid)
5//! - [PUT /_matrix/federation/v1/send_leave/{roomId}/{eventId}](https://spec.matrix.org/latest/server-server-api/#put_matrixfederationv1send_leaveroomideventid)
6//!
7//! For more information, see this [GitHub issue][issue].
8//!
9//! [issue]: https://github.com/matrix-org/matrix-spec-proposals/issues/2541
10
11#[cfg(feature = "client")]
12use std::{fmt, marker::PhantomData};
13
14#[cfg(feature = "client")]
15use serde::de::{Deserialize, Deserializer, Error, IgnoredAny, SeqAccess, Visitor};
16#[cfg(feature = "server")]
17use serde::ser::{Serialize, SerializeSeq, Serializer};
18
19#[cfg(feature = "server")]
20pub(crate) fn serialize<T, S>(val: &T, serializer: S) -> Result<S::Ok, S::Error>
21where
22    S: Serializer,
23    T: Serialize,
24{
25    let mut seq = serializer.serialize_seq(Some(2))?;
26    seq.serialize_element(&200)?;
27    seq.serialize_element(val)?;
28    seq.end()
29}
30
31#[cfg(feature = "client")]
32pub(crate) fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
33where
34    D: Deserializer<'de>,
35    T: Deserialize<'de>,
36{
37    deserializer.deserialize_seq(PduVisitor { phantom: PhantomData })
38}
39
40#[cfg(feature = "client")]
41struct PduVisitor<T> {
42    phantom: PhantomData<T>,
43}
44
45#[cfg(feature = "client")]
46impl<'de, T> Visitor<'de> for PduVisitor<T>
47where
48    T: Deserialize<'de>,
49{
50    type Value = T;
51
52    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
53        formatter.write_str("a PDU wrapped in an array.")
54    }
55
56    fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
57    where
58        A: SeqAccess<'de>,
59    {
60        let expected = "a two-element list in the response";
61        if seq.next_element::<IgnoredAny>()?.is_none() {
62            return Err(A::Error::invalid_length(0, &expected));
63        }
64
65        let val = seq.next_element()?.ok_or_else(|| A::Error::invalid_length(1, &expected))?;
66
67        while let Some(IgnoredAny) = seq.next_element()? {
68            // ignore extra elements
69        }
70
71        Ok(val)
72    }
73}
74
75#[cfg(all(test, feature = "client"))]
76mod tests_client {
77    use assert_matches2::assert_matches;
78    use serde_json::json;
79
80    use super::deserialize;
81    #[allow(deprecated)]
82    use crate::membership::create_join_event::v1::RoomState;
83
84    #[test]
85    fn deserialize_response() {
86        let response = json!([
87            200,
88            {
89                "auth_chain": [],
90                "state": []
91            }
92        ]);
93
94        #[allow(deprecated)]
95        let RoomState { auth_chain, state, event } = deserialize(response).unwrap();
96        assert_matches!(auth_chain.as_slice(), []);
97        assert_matches!(state.as_slice(), []);
98        assert_matches!(event, None);
99    }
100
101    #[test]
102    fn too_short_array() {
103        let json = json!([200]);
104        #[allow(deprecated)]
105        let failed_room_state = deserialize::<RoomState, _>(json);
106        assert_eq!(
107            failed_room_state.unwrap_err().to_string(),
108            "invalid length 1, expected a two-element list in the response"
109        );
110    }
111
112    #[test]
113    fn not_an_array() {
114        let json = json!({
115            "origin": "matrix.org",
116            "auth_chain": [],
117            "state": []
118        });
119        #[allow(deprecated)]
120        let failed_room_state = deserialize::<RoomState, _>(json);
121
122        assert_eq!(
123            failed_room_state.unwrap_err().to_string(),
124            "invalid type: map, expected a PDU wrapped in an array.",
125        );
126    }
127
128    #[test]
129    fn too_long_array() {
130        let json = json!([200, { "auth_chain": [], "state": [] }, 200]);
131        #[allow(deprecated)]
132        let RoomState { auth_chain, state, event } = deserialize(json).unwrap();
133        assert_matches!(auth_chain.as_slice(), []);
134        assert_matches!(state.as_slice(), []);
135        assert_matches!(event, None);
136    }
137}
138
139#[cfg(all(test, feature = "server"))]
140mod tests_server {
141    use serde_json::json;
142
143    use super::serialize;
144    #[allow(deprecated)]
145    use crate::membership::create_join_event::v1::RoomState;
146
147    #[test]
148    fn serialize_response() {
149        #[allow(deprecated)]
150        let room_state = RoomState { auth_chain: Vec::new(), state: Vec::new(), event: None };
151
152        let serialized = serialize(&room_state, serde_json::value::Serializer).unwrap();
153        let expected = json!(
154            [
155                200,
156                {
157                    "auth_chain": [],
158                    "state": []
159                }
160            ]
161        );
162
163        assert_eq!(serialized, expected);
164    }
165}