1use std::borrow::Cow;
6
7use ruma_common::serde::JsonObject;
8use serde::{Deserialize, Deserializer, Serialize};
9
10pub mod transports;
11
12#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
14#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
15#[serde(tag = "type")]
16pub enum RtcTransport {
17 #[cfg(feature = "unstable-msc4195")]
19 #[serde(rename = "livekit")]
20 LiveKit(LiveKitRtcTransport),
21
22 #[doc(hidden)]
24 #[serde(untagged)]
25 _Custom(CustomRtcTransport),
26}
27
28impl<'de> Deserialize<'de> for RtcTransport {
29 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
30 where
31 D: Deserializer<'de>,
32 {
33 use serde::de::Error;
34
35 let mut obj = JsonObject::deserialize(deserializer)?;
36 let transport_type = match obj.remove("type") {
37 Some(serde_json::Value::String(s)) => s,
38 Some(_) => return Err(D::Error::custom("`type` must be a string")),
39 None => return Err(D::Error::missing_field("type")),
40 };
41
42 Ok(match transport_type.as_str() {
43 #[cfg(feature = "unstable-msc4195")]
44 "livekit" => Self::LiveKit(
45 serde_json::from_value(serde_json::Value::Object(obj)).map_err(D::Error::custom)?,
46 ),
47 _ => Self::_Custom(CustomRtcTransport { transport_type, data: obj }),
48 })
49 }
50}
51
52impl RtcTransport {
53 pub fn new(transport_type: &str, data: JsonObject) -> serde_json::Result<Self> {
64 Ok(match transport_type {
65 #[cfg(feature = "unstable-msc4195")]
66 "livekit" => Self::LiveKit(serde_json::from_value(serde_json::Value::Object(data))?),
67 _ => Self::_Custom(CustomRtcTransport {
68 transport_type: transport_type.to_owned(),
69 data,
70 }),
71 })
72 }
73
74 #[cfg(feature = "unstable-msc4195")]
75 pub fn livekit(service_url: String) -> Self {
77 Self::LiveKit(LiveKitRtcTransport { service_url })
78 }
79
80 pub fn transport_type(&self) -> &str {
82 match self {
83 #[cfg(feature = "unstable-msc4195")]
84 Self::LiveKit(_) => "livekit",
85 Self::_Custom(custom) => &custom.transport_type,
86 }
87 }
88
89 pub fn data(&self) -> Cow<'_, JsonObject> {
97 #[cfg(feature = "unstable-msc4195")]
98 fn serialize<T: Serialize>(object: &T) -> JsonObject {
99 use serde_json::Value as JsonValue;
100
101 match serde_json::to_value(object).expect("rtc transport type serialization to succeed")
102 {
103 JsonValue::Object(object) => object,
104 _ => panic!("all rtc transport types must serialize to objects"),
105 }
106 }
107
108 match self {
109 #[cfg(feature = "unstable-msc4195")]
110 Self::LiveKit(info) => Cow::Owned(serialize(info)),
111 Self::_Custom(info) => Cow::Borrowed(&info.data),
112 }
113 }
114}
115
116#[cfg(feature = "unstable-msc4195")]
118#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
119#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
120pub struct LiveKitRtcTransport {
121 #[serde(rename = "livekit_service_url")]
123 pub service_url: String,
124}
125
126#[cfg(feature = "unstable-msc4195")]
127impl LiveKitRtcTransport {
128 pub fn new(service_url: String) -> Self {
130 Self { service_url }
131 }
132}
133
134#[cfg(feature = "unstable-msc4195")]
135impl From<LiveKitRtcTransport> for RtcTransport {
136 fn from(value: LiveKitRtcTransport) -> Self {
137 Self::LiveKit(value)
138 }
139}
140
141#[doc(hidden)]
147#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
148pub struct CustomRtcTransport {
149 #[serde(rename = "type")]
151 transport_type: String,
152
153 #[serde(flatten)]
155 data: JsonObject,
156}
157
158#[cfg(test)]
159mod tests {
160 use assert_matches2::assert_matches;
161 use serde_json::{
162 Value as JsonValue, from_value as from_json_value, json, to_value as to_json_value,
163 };
164
165 use super::RtcTransport;
166
167 #[test]
168 fn serialize_roundtrip_custom_rtc_transport() {
169 let transport_type = "local.custom.transport";
170 assert_matches!(
171 json!({
172 "foo": "bar",
173 "baz": true,
174 }),
175 JsonValue::Object(transport_data)
176 );
177 let transport = RtcTransport::new(transport_type, transport_data.clone()).unwrap();
178 let json = json!({
179 "type": transport_type,
180 "foo": "bar",
181 "baz": true,
182 });
183
184 assert_eq!(transport.transport_type(), transport_type);
185 assert_eq!(*transport.data().as_ref(), transport_data);
186 assert_eq!(to_json_value(&transport).unwrap(), json);
187 assert_eq!(from_json_value::<RtcTransport>(json).unwrap(), transport);
188 }
189
190 #[cfg(feature = "unstable-msc4195")]
191 #[test]
192 fn livekit_transport_new_and_from_impl() {
193 use super::LiveKitRtcTransport;
194
195 let url = "http://livekit.local/".to_owned();
196 let inner = LiveKitRtcTransport::new(url.clone());
197 let transport = RtcTransport::from(inner);
198 assert_eq!(transport.transport_type(), "livekit");
199 assert_eq!(transport, RtcTransport::livekit(url));
200 }
201
202 #[cfg(feature = "unstable-msc4195")]
203 #[test]
204 fn serialize_roundtrip_livekit_sfu_transport() {
205 let transport_type = "livekit";
206 let livekit_service_url = "http://livekit.local/";
207 let transport = RtcTransport::livekit(livekit_service_url.to_owned());
208 let json = json!({
209 "type": transport_type,
210 "livekit_service_url": livekit_service_url,
211 });
212
213 assert_eq!(transport.transport_type(), transport_type);
214 assert_eq!(to_json_value(&transport).unwrap(), json);
215 assert_eq!(from_json_value::<RtcTransport>(json).unwrap(), transport);
216 }
217}