Skip to main content

ruma_client_api/rtc/
transports.rs

1//! `GET /_matrix/client/*/rtc/transports`
2//!
3//! Discover the RTC transports advertised by the homeserver.
4
5pub mod v1 {
6    //! `/v1/` ([MSC])
7    //!
8    //! [MSC]: https://github.com/matrix-org/matrix-spec-proposals/pull/4143
9
10    use std::borrow::Cow;
11
12    use ruma_common::{
13        api::{auth_scheme::AccessToken, request, response},
14        metadata,
15        serde::{JsonObject, from_raw_json_value},
16    };
17    use serde::{Deserialize, Deserializer, Serialize, de::DeserializeOwned};
18    use serde_json::{Value as JsonValue, value::RawValue as RawJsonValue};
19
20    metadata! {
21        method: GET,
22        rate_limited: false,
23        authentication: AccessToken,
24        history: {
25            unstable => "/_matrix/client/unstable/org.matrix.msc4143/rtc/transports",
26        }
27    }
28
29    /// Request type for the `transports` endpoint.
30    #[request]
31    #[derive(Default)]
32    pub struct Request {}
33
34    impl Request {
35        /// Creates a new empty `Request`.
36        pub fn new() -> Self {
37            Self {}
38        }
39    }
40
41    /// Response type for the `transports` endpoint.
42    #[response]
43    #[derive(Default)]
44    pub struct Response {
45        /// The RTC transports advertised by the homeserver.
46        pub rtc_transports: Vec<RtcTransport>,
47    }
48
49    impl Response {
50        /// Creates a `Response` with the given RTC transports.
51        pub fn new(rtc_transports: Vec<RtcTransport>) -> Self {
52            Self { rtc_transports }
53        }
54    }
55
56    /// A MatrixRTC transport.
57    ///
58    /// This type can hold arbitrary RTC transports. Their data can be accessed with
59    /// [`transport_type()`](Self::transport_type) and [`data()`](Self::data()).
60    #[derive(Clone, Debug, Serialize, PartialEq, Eq)]
61    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
62    #[serde(tag = "type")]
63    pub enum RtcTransport {
64        /// A LiveKit multi-SFU transport.
65        #[cfg(feature = "unstable-msc4195")]
66        #[serde(rename = "livekit_multi_sfu")]
67        LivekitMultiSfu(LivekitMultiSfuTransport),
68
69        /// An unsupported transport.
70        #[doc(hidden)]
71        #[serde(untagged)]
72        _Custom(CustomRtcTransport),
73    }
74
75    impl RtcTransport {
76        /// A constructor to create a custom RTC transport.
77        ///
78        /// Prefer to use the public variants of `RtcTransport` where possible; this constructor is
79        /// meant to be used for unsupported focus types only and does not allow setting arbitrary
80        /// data for supported ones.
81        ///
82        /// # Errors
83        ///
84        /// Returns an error if the `transport_type` is known and deserialization of `data` to the
85        /// corresponding `RtcTransport` variant fails.
86        pub fn new(transport_type: String, data: JsonObject) -> serde_json::Result<Self> {
87            fn deserialize_variant<T: DeserializeOwned>(obj: JsonObject) -> serde_json::Result<T> {
88                serde_json::from_value(obj.into())
89            }
90
91            Ok(match transport_type.as_str() {
92                #[cfg(feature = "unstable-msc4195")]
93                "livekit_multi_sfu" => Self::LivekitMultiSfu(deserialize_variant(data)?),
94                _ => Self::_Custom(CustomRtcTransport { transport_type, data }),
95            })
96        }
97
98        /// Returns a reference to the type of this RTC transport.
99        pub fn transport_type(&self) -> &str {
100            match self {
101                #[cfg(feature = "unstable-msc4195")]
102                Self::LivekitMultiSfu(_) => "livekit_multi_sfu",
103                Self::_Custom(custom) => &custom.transport_type,
104            }
105        }
106
107        /// Returns the associated data.
108        ///
109        /// The returned JSON object won't contain the `type` field, please use
110        /// [`transport_type()`][Self::transport_type] to access that.
111        ///
112        /// Prefer to use the public variants of `RtcTransport` where possible; this method is meant
113        /// to be used for custom focus types only.
114        pub fn data(&self) -> Cow<'_, JsonObject> {
115            fn serialize<T: Serialize>(object: &T) -> JsonObject {
116                match serde_json::to_value(object).expect("rtc focus type serialization to succeed")
117                {
118                    JsonValue::Object(object) => object,
119                    _ => panic!("rtc transports must serialize to JSON objects"),
120                }
121            }
122
123            match self {
124                #[cfg(feature = "unstable-msc4195")]
125                Self::LivekitMultiSfu(info) => Cow::Owned(serialize(info)),
126                Self::_Custom(info) => Cow::Borrowed(&info.data),
127            }
128        }
129    }
130
131    impl<'de> Deserialize<'de> for RtcTransport {
132        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
133        where
134            D: Deserializer<'de>,
135        {
136            #[derive(Deserialize)]
137            struct RtcTransportDeHelper {
138                #[serde(rename = "type")]
139                transport_type: String,
140            }
141
142            let json = Box::<RawJsonValue>::deserialize(deserializer)?;
143            let RtcTransportDeHelper { transport_type } = from_raw_json_value(&json)?;
144
145            Ok(match transport_type.as_str() {
146                #[cfg(feature = "unstable-msc4195")]
147                "livekit_multi_sfu" => Self::LivekitMultiSfu(from_raw_json_value(&json)?),
148                _ => {
149                    let mut data = from_raw_json_value::<JsonObject, _>(&json)?;
150                    data.remove("type");
151
152                    Self::_Custom(CustomRtcTransport { transport_type, data })
153                }
154            })
155        }
156    }
157
158    /// A LiveKit multi-SFU transport.
159    #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
160    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
161    #[cfg(feature = "unstable-msc4195")]
162    pub struct LivekitMultiSfuTransport {
163        /// The URL for the LiveKit service.
164        pub livekit_service_url: String,
165    }
166
167    #[cfg(feature = "unstable-msc4195")]
168    impl LivekitMultiSfuTransport {
169        /// Construct a new `LivekitMultiSfuTransport` with the given LiveKit service URL.
170        pub fn new(livekit_service_url: String) -> Self {
171            Self { livekit_service_url }
172        }
173    }
174
175    #[cfg(feature = "unstable-msc4195")]
176    impl From<LivekitMultiSfuTransport> for RtcTransport {
177        fn from(value: LivekitMultiSfuTransport) -> Self {
178            Self::LivekitMultiSfu(value)
179        }
180    }
181
182    #[cfg(feature = "unstable-msc4195")]
183    impl From<LivekitMultiSfuTransport> for crate::discovery::discover_homeserver::LiveKitRtcFocusInfo {
184        fn from(value: LivekitMultiSfuTransport) -> Self {
185            let LivekitMultiSfuTransport { livekit_service_url } = value;
186            Self { service_url: livekit_service_url }
187        }
188    }
189
190    #[cfg(feature = "unstable-msc4195")]
191    impl From<crate::discovery::discover_homeserver::LiveKitRtcFocusInfo> for LivekitMultiSfuTransport {
192        fn from(value: crate::discovery::discover_homeserver::LiveKitRtcFocusInfo) -> Self {
193            let crate::discovery::discover_homeserver::LiveKitRtcFocusInfo { service_url } = value;
194            Self { livekit_service_url: service_url }
195        }
196    }
197
198    /// Information about an unsupported RTC transport.
199    #[doc(hidden)]
200    #[derive(Clone, Debug, Serialize, PartialEq, Eq)]
201    pub struct CustomRtcTransport {
202        /// The type of RTC transport.
203        #[serde(rename = "type")]
204        transport_type: String,
205
206        /// Remaining data.
207        #[serde(flatten)]
208        data: JsonObject,
209    }
210}
211
212#[cfg(test)]
213mod tests {
214    use assert_matches2::assert_let;
215    use ruma_common::canonical_json::assert_to_canonical_json_eq;
216    use serde_json::{Value as JsonValue, from_value as from_json_value, json};
217
218    use super::v1::{LivekitMultiSfuTransport, RtcTransport};
219
220    #[test]
221    fn serialize_roundtrip_custom_rtc_transport() {
222        let transport_type = "local.custom.transport";
223        assert_let!(
224            JsonValue::Object(transport_data) = json!({
225                "foo": "bar",
226                "baz": true,
227            })
228        );
229        let transport =
230            RtcTransport::new(transport_type.to_owned(), transport_data.clone()).unwrap();
231        let json = json!({
232            "type": transport_type,
233            "foo": "bar",
234            "baz": true,
235        });
236
237        assert_eq!(transport.transport_type(), transport_type);
238        assert_eq!(*transport.data().as_ref(), transport_data);
239        assert_to_canonical_json_eq!(transport, json.clone());
240        assert_eq!(from_json_value::<RtcTransport>(json).unwrap(), transport);
241    }
242
243    #[cfg(feature = "unstable-msc4195")]
244    #[test]
245    fn serialize_roundtrip_livekit_multi_sfu_transport() {
246        let transport_type = "livekit_multi_sfu";
247        let livekit_service_url = "http://livekit.local/";
248        let transport =
249            RtcTransport::from(LivekitMultiSfuTransport::new(livekit_service_url.to_owned()));
250        let json = json!({
251            "type": transport_type,
252            "livekit_service_url": livekit_service_url,
253        });
254
255        assert_eq!(transport.transport_type(), transport_type);
256        assert_to_canonical_json_eq!(transport, json.clone());
257        assert_eq!(from_json_value::<RtcTransport>(json).unwrap(), transport);
258    }
259}