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::NoAuthentication, request, response},
14 metadata,
15};
16#[cfg(feature = "unstable-msc4143")]
17use serde::de::DeserializeOwned;
18use serde::{Deserialize, Serialize};
19#[cfg(feature = "unstable-msc4143")]
20use serde_json::Value as JsonValue;
21
22metadata! {
23 method: GET,
24 rate_limited: false,
25 authentication: NoAuthentication,
26 path: "/.well-known/matrix/client",
27}
28
29#[request(error = crate::Error)]
31#[derive(Default)]
32pub struct Request {}
33
34#[response(error = crate::Error)]
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, Deserialize, 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")]
211#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
212#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
213pub struct LiveKitRtcFocusInfo {
214 #[serde(rename = "livekit_service_url")]
216 pub service_url: String,
217}
218
219#[doc(hidden)]
221#[cfg(feature = "unstable-msc4143")]
222#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
223pub struct CustomRtcFocusInfo {
224 #[serde(rename = "type")]
226 focus_type: String,
227
228 #[serde(flatten)]
230 data: JsonObject,
231}
232
233#[cfg(test)]
234mod tests {
235 #[cfg(feature = "unstable-msc4143")]
236 use assert_matches2::assert_matches;
237 #[cfg(feature = "unstable-msc4143")]
238 use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
239
240 #[cfg(feature = "unstable-msc4143")]
241 use super::RtcFocusInfo;
242
243 #[test]
244 #[cfg(feature = "unstable-msc4143")]
245 fn test_livekit_rtc_focus_deserialization() {
246 let json = json!({
248 "type": "livekit",
249 "livekit_service_url": "https://livekit.example.com"
250 });
251
252 let focus: RtcFocusInfo = from_json_value(json).unwrap();
254
255 assert_matches!(focus, RtcFocusInfo::LiveKit(info));
257 assert_eq!(info.service_url, "https://livekit.example.com");
258 }
259
260 #[test]
261 #[cfg(feature = "unstable-msc4143")]
262 fn test_livekit_rtc_focus_serialization() {
263 let focus = RtcFocusInfo::livekit("https://livekit.example.com".to_owned());
265
266 let json = to_json_value(&focus).unwrap();
268
269 assert_eq!(
271 json,
272 json!({
273 "type": "livekit",
274 "livekit_service_url": "https://livekit.example.com"
275 })
276 );
277 }
278
279 #[test]
280 #[cfg(feature = "unstable-msc4143")]
281 fn test_custom_rtc_focus_serialization() {
282 let json = json!({
284 "type": "some-focus-type",
285 "additional-type-specific-field": "https://my_focus.domain",
286 "another-additional-type-specific-field": ["with", "Array", "type"]
287 });
288
289 let focus: RtcFocusInfo = from_json_value(json.clone()).unwrap();
291
292 assert_eq!(focus.focus_type(), "some-focus-type");
295
296 let data = &focus.data();
297 assert_eq!(data["additional-type-specific-field"], "https://my_focus.domain");
298
299 let array_values: Vec<&str> = data["another-additional-type-specific-field"]
300 .as_array()
301 .unwrap()
302 .iter()
303 .map(|v| v.as_str().unwrap())
304 .collect();
305 assert_eq!(array_values, vec!["with", "Array", "type"]);
306
307 assert!(!data.contains_key("type"));
308
309 let serialized = to_json_value(&focus).unwrap();
311
312 assert_eq!(serialized, json);
314 }
315}