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,
16    };
17    use serde::{Deserialize, Serialize, de::DeserializeOwned};
18    use serde_json::Value as JsonValue;
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(error = crate::Error)]
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(error = crate::Error)]
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, Deserialize, 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    /// A LiveKit multi-SFU transport.
132    #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
133    #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
134    #[cfg(feature = "unstable-msc4195")]
135    pub struct LivekitMultiSfuTransport {
136        /// The URL for the LiveKit service.
137        pub livekit_service_url: String,
138    }
139
140    #[cfg(feature = "unstable-msc4195")]
141    impl LivekitMultiSfuTransport {
142        /// Construct a new `LivekitMultiSfuTransport` with the given LiveKit service URL.
143        pub fn new(livekit_service_url: String) -> Self {
144            Self { livekit_service_url }
145        }
146    }
147
148    #[cfg(feature = "unstable-msc4195")]
149    impl From<LivekitMultiSfuTransport> for RtcTransport {
150        fn from(value: LivekitMultiSfuTransport) -> Self {
151            Self::LivekitMultiSfu(value)
152        }
153    }
154
155    #[cfg(feature = "unstable-msc4195")]
156    impl From<LivekitMultiSfuTransport> for crate::discovery::discover_homeserver::LiveKitRtcFocusInfo {
157        fn from(value: LivekitMultiSfuTransport) -> Self {
158            let LivekitMultiSfuTransport { livekit_service_url } = value;
159            Self { service_url: livekit_service_url }
160        }
161    }
162
163    #[cfg(feature = "unstable-msc4195")]
164    impl From<crate::discovery::discover_homeserver::LiveKitRtcFocusInfo> for LivekitMultiSfuTransport {
165        fn from(value: crate::discovery::discover_homeserver::LiveKitRtcFocusInfo) -> Self {
166            let crate::discovery::discover_homeserver::LiveKitRtcFocusInfo { service_url } = value;
167            Self { livekit_service_url: service_url }
168        }
169    }
170
171    /// Information about an unsupported RTC transport.
172    #[doc(hidden)]
173    #[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
174    pub struct CustomRtcTransport {
175        /// The type of RTC transport.
176        #[serde(rename = "type")]
177        transport_type: String,
178
179        /// Remaining data.
180        #[serde(flatten)]
181        data: JsonObject,
182    }
183}
184
185#[cfg(test)]
186mod tests {
187    use assert_matches2::assert_matches;
188    use serde_json::{
189        Value as JsonValue, from_value as from_json_value, json, to_value as to_json_value,
190    };
191
192    use super::v1::{LivekitMultiSfuTransport, RtcTransport};
193
194    #[test]
195    fn serialize_roundtrip_custom_rtc_transport() {
196        let transport_type = "local.custom.transport";
197        assert_matches!(
198            json!({
199                "foo": "bar",
200                "baz": true,
201            }),
202            JsonValue::Object(transport_data)
203        );
204        let transport =
205            RtcTransport::new(transport_type.to_owned(), transport_data.clone()).unwrap();
206        let json = json!({
207            "type": transport_type,
208            "foo": "bar",
209            "baz": true,
210        });
211
212        assert_eq!(transport.transport_type(), transport_type);
213        assert_eq!(*transport.data().as_ref(), transport_data);
214        assert_eq!(to_json_value(&transport).unwrap(), json);
215        assert_eq!(from_json_value::<RtcTransport>(json).unwrap(), transport);
216    }
217
218    #[cfg(feature = "unstable-msc4195")]
219    #[test]
220    fn serialize_roundtrip_livekit_multi_sfu_transport() {
221        let transport_type = "livekit_multi_sfu";
222        let livekit_service_url = "http://livekit.local/";
223        let transport =
224            RtcTransport::from(LivekitMultiSfuTransport::new(livekit_service_url.to_owned()));
225        let json = json!({
226            "type": transport_type,
227            "livekit_service_url": livekit_service_url,
228        });
229
230        assert_eq!(transport.transport_type(), transport_type);
231        assert_eq!(to_json_value(&transport).unwrap(), json);
232        assert_eq!(from_json_value::<RtcTransport>(json).unwrap(), transport);
233    }
234}