ruma_client_api/discovery/
discover_homeserver.rs1#[cfg(feature = "unstable-msc4143")]
8use std::borrow::Cow;
9
10#[cfg(feature = "unstable-msc4143")]
11use ruma_common::serde::JsonObject;
12use ruma_common::{
13 api::{auth_scheme::NoAccessToken, request, response},
14 metadata,
15};
16use serde::{Deserialize, Serialize};
17#[cfg(feature = "unstable-msc4143")]
18use serde::{Deserializer, de, de::DeserializeOwned};
19#[cfg(feature = "unstable-msc4143")]
20use serde_json::Value as JsonValue;
21
22metadata! {
23 method: GET,
24 rate_limited: false,
25 authentication: NoAccessToken,
26 path: "/.well-known/matrix/client",
27}
28
29#[request]
31#[derive(Default)]
32pub struct Request {}
33
34#[response]
36pub struct Response {
37 #[serde(rename = "m.homeserver")]
39 pub homeserver: HomeserverInfo,
40
41 #[serde(rename = "m.identity_server", skip_serializing_if = "Option::is_none")]
43 pub identity_server: Option<IdentityServerInfo>,
44
45 #[cfg(feature = "unstable-msc3488")]
47 #[serde(
48 rename = "org.matrix.msc3488.tile_server",
49 alias = "m.tile_server",
50 skip_serializing_if = "Option::is_none"
51 )]
52 pub tile_server: Option<TileServerInfo>,
53
54 #[cfg(feature = "unstable-msc4143")]
56 #[serde(
57 rename = "org.matrix.msc4143.rtc_foci",
58 alias = "m.rtc_foci",
59 default,
60 skip_serializing_if = "Vec::is_empty"
61 )]
62 pub rtc_foci: Vec<RtcFocusInfo>,
63}
64
65impl Request {
66 pub fn new() -> Self {
68 Self {}
69 }
70}
71
72impl Response {
73 pub fn new(homeserver: HomeserverInfo) -> Self {
75 Self {
76 homeserver,
77 identity_server: None,
78 #[cfg(feature = "unstable-msc3488")]
79 tile_server: None,
80 #[cfg(feature = "unstable-msc4143")]
81 rtc_foci: Default::default(),
82 }
83 }
84}
85
86#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
88#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
89pub struct HomeserverInfo {
90 pub base_url: String,
92}
93
94impl HomeserverInfo {
95 pub fn new(base_url: String) -> Self {
97 Self { base_url }
98 }
99}
100
101#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
103#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
104pub struct IdentityServerInfo {
105 pub base_url: String,
107}
108
109impl IdentityServerInfo {
110 pub fn new(base_url: String) -> Self {
112 Self { base_url }
113 }
114}
115
116#[cfg(feature = "unstable-msc3488")]
118#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
119#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
120pub struct TileServerInfo {
121 pub map_style_url: String,
125}
126
127#[cfg(feature = "unstable-msc3488")]
128impl TileServerInfo {
129 pub fn new(map_style_url: String) -> Self {
131 Self { map_style_url }
132 }
133}
134
135#[cfg(feature = "unstable-msc4143")]
137#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
138#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
139#[serde(tag = "type")]
140pub enum RtcFocusInfo {
141 #[serde(rename = "livekit")]
143 LiveKit(LiveKitRtcFocusInfo),
144
145 #[doc(hidden)]
147 #[serde(untagged)]
148 _Custom(CustomRtcFocusInfo),
149}
150
151#[cfg(feature = "unstable-msc4143")]
152impl RtcFocusInfo {
153 pub fn new(focus_type: &str, data: JsonObject) -> serde_json::Result<Self> {
164 fn deserialize_variant<T: DeserializeOwned>(obj: JsonObject) -> serde_json::Result<T> {
165 serde_json::from_value(JsonValue::Object(obj))
166 }
167
168 Ok(match focus_type {
169 "livekit" => Self::LiveKit(deserialize_variant(data)?),
170 _ => Self::_Custom(CustomRtcFocusInfo { focus_type: focus_type.to_owned(), data }),
171 })
172 }
173
174 pub fn livekit(service_url: String) -> Self {
176 Self::LiveKit(LiveKitRtcFocusInfo { service_url })
177 }
178
179 pub fn focus_type(&self) -> &str {
181 match self {
182 Self::LiveKit(_) => "livekit",
183 Self::_Custom(custom) => &custom.focus_type,
184 }
185 }
186
187 pub fn data(&self) -> Cow<'_, JsonObject> {
195 fn serialize<T: Serialize>(object: &T) -> JsonObject {
196 match serde_json::to_value(object).expect("rtc focus type serialization to succeed") {
197 JsonValue::Object(object) => object,
198 _ => panic!("all rtc focus types must serialize to objects"),
199 }
200 }
201
202 match self {
203 Self::LiveKit(info) => Cow::Owned(serialize(info)),
204 Self::_Custom(info) => Cow::Borrowed(&info.data),
205 }
206 }
207}
208
209#[cfg(feature = "unstable-msc4143")]
210impl<'de> Deserialize<'de> for RtcFocusInfo {
211 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
212 where
213 D: Deserializer<'de>,
214 {
215 use as_variant::as_variant;
216
217 let mut data = JsonObject::deserialize(deserializer)?;
218 let focus_type = data
219 .remove("type")
220 .and_then(|value| as_variant!(value, JsonValue::String))
221 .ok_or_else(|| de::Error::missing_field("type"))?;
222
223 match focus_type.as_ref() {
224 "livekit" => serde_json::from_value(data.into()).map(Self::LiveKit),
225 _ => Ok(Self::_Custom(CustomRtcFocusInfo { focus_type, data })),
226 }
227 .map_err(de::Error::custom)
228 }
229}
230
231#[cfg(feature = "unstable-msc4143")]
233#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
234#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
235pub struct LiveKitRtcFocusInfo {
236 #[serde(rename = "livekit_service_url")]
238 pub service_url: String,
239}
240
241#[doc(hidden)]
243#[cfg(feature = "unstable-msc4143")]
244#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
245pub struct CustomRtcFocusInfo {
246 #[serde(rename = "type")]
248 focus_type: String,
249
250 #[serde(flatten)]
252 data: JsonObject,
253}
254
255#[cfg(test)]
256mod tests {
257 #[cfg(feature = "unstable-msc4143")]
258 use assert_matches2::assert_let;
259 #[cfg(feature = "unstable-msc4143")]
260 use ruma_common::canonical_json::assert_to_canonical_json_eq;
261 #[cfg(feature = "unstable-msc4143")]
262 use serde_json::{from_value as from_json_value, json};
263
264 #[cfg(feature = "unstable-msc4143")]
265 use super::RtcFocusInfo;
266
267 #[test]
268 #[cfg(feature = "unstable-msc4143")]
269 fn test_livekit_rtc_focus_deserialization() {
270 let json = json!({
272 "type": "livekit",
273 "livekit_service_url": "https://livekit.example.com"
274 });
275
276 let focus: RtcFocusInfo = from_json_value(json).unwrap();
278
279 assert_let!(RtcFocusInfo::LiveKit(info) = focus);
281 assert_eq!(info.service_url, "https://livekit.example.com");
282 }
283
284 #[test]
285 #[cfg(feature = "unstable-msc4143")]
286 fn test_livekit_rtc_focus_serialization() {
287 let focus = RtcFocusInfo::livekit("https://livekit.example.com".to_owned());
289
290 assert_to_canonical_json_eq!(
292 focus,
293 json!({
294 "type": "livekit",
295 "livekit_service_url": "https://livekit.example.com"
296 })
297 );
298 }
299
300 #[test]
301 #[cfg(feature = "unstable-msc4143")]
302 fn test_custom_rtc_focus_serialization() {
303 let json = json!({
305 "type": "some-focus-type",
306 "additional-type-specific-field": "https://my_focus.domain",
307 "another-additional-type-specific-field": ["with", "Array", "type"]
308 });
309
310 let focus: RtcFocusInfo = from_json_value(json.clone()).unwrap();
312
313 assert_eq!(focus.focus_type(), "some-focus-type");
316
317 let data = &focus.data();
318 assert_eq!(data["additional-type-specific-field"], "https://my_focus.domain");
319
320 let array_values: Vec<&str> = data["another-additional-type-specific-field"]
321 .as_array()
322 .unwrap()
323 .iter()
324 .map(|v| v.as_str().unwrap())
325 .collect();
326 assert_eq!(array_values, vec!["with", "Array", "type"]);
327
328 assert!(!data.contains_key("type"));
329
330 assert_to_canonical_json_eq!(focus, json);
332 }
333}