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