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, from_raw_json_value};
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::DeserializeOwned};
19#[cfg(feature = "unstable-msc4143")]
20use serde_json::Value as JsonValue;
21#[cfg(feature = "unstable-msc4143")]
22use serde_json::value::RawValue as RawJsonValue;
23
24metadata! {
25 method: GET,
26 rate_limited: false,
27 authentication: NoAccessToken,
28 path: "/.well-known/matrix/client",
29}
30
31#[request]
33#[derive(Default)]
34pub struct Request {}
35
36#[response]
38pub struct Response {
39 #[serde(rename = "m.homeserver")]
41 pub homeserver: HomeserverInfo,
42
43 #[serde(rename = "m.identity_server", skip_serializing_if = "Option::is_none")]
45 pub identity_server: Option<IdentityServerInfo>,
46
47 #[cfg(feature = "unstable-msc3488")]
49 #[serde(
50 rename = "org.matrix.msc3488.tile_server",
51 alias = "m.tile_server",
52 skip_serializing_if = "Option::is_none"
53 )]
54 pub tile_server: Option<TileServerInfo>,
55
56 #[cfg(feature = "unstable-msc4143")]
58 #[serde(
59 rename = "org.matrix.msc4143.rtc_foci",
60 alias = "m.rtc_foci",
61 default,
62 skip_serializing_if = "Vec::is_empty"
63 )]
64 pub rtc_foci: Vec<RtcFocusInfo>,
65}
66
67impl Request {
68 pub fn new() -> Self {
70 Self {}
71 }
72}
73
74impl Response {
75 pub fn new(homeserver: HomeserverInfo) -> Self {
77 Self {
78 homeserver,
79 identity_server: None,
80 #[cfg(feature = "unstable-msc3488")]
81 tile_server: None,
82 #[cfg(feature = "unstable-msc4143")]
83 rtc_foci: Default::default(),
84 }
85 }
86}
87
88#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
90#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
91pub struct HomeserverInfo {
92 pub base_url: String,
94}
95
96impl HomeserverInfo {
97 pub fn new(base_url: String) -> Self {
99 Self { base_url }
100 }
101}
102
103#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
105#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
106pub struct IdentityServerInfo {
107 pub base_url: String,
109}
110
111impl IdentityServerInfo {
112 pub fn new(base_url: String) -> Self {
114 Self { base_url }
115 }
116}
117
118#[cfg(feature = "unstable-msc3488")]
120#[derive(Clone, Debug, Deserialize, Hash, Serialize, PartialEq, Eq)]
121#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
122pub struct TileServerInfo {
123 pub map_style_url: String,
127}
128
129#[cfg(feature = "unstable-msc3488")]
130impl TileServerInfo {
131 pub fn new(map_style_url: String) -> Self {
133 Self { map_style_url }
134 }
135}
136
137#[cfg(feature = "unstable-msc4143")]
139#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
140#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
141#[serde(tag = "type")]
142pub enum RtcFocusInfo {
143 #[serde(rename = "livekit")]
145 LiveKit(LiveKitRtcFocusInfo),
146
147 #[doc(hidden)]
149 #[serde(untagged)]
150 _Custom(CustomRtcFocusInfo),
151}
152
153#[cfg(feature = "unstable-msc4143")]
154impl RtcFocusInfo {
155 pub fn new(focus_type: &str, data: JsonObject) -> serde_json::Result<Self> {
166 fn deserialize_variant<T: DeserializeOwned>(obj: JsonObject) -> serde_json::Result<T> {
167 serde_json::from_value(JsonValue::Object(obj))
168 }
169
170 Ok(match focus_type {
171 "livekit" => Self::LiveKit(deserialize_variant(data)?),
172 _ => Self::_Custom(CustomRtcFocusInfo { focus_type: focus_type.to_owned(), data }),
173 })
174 }
175
176 pub fn livekit(service_url: String) -> Self {
178 Self::LiveKit(LiveKitRtcFocusInfo { service_url })
179 }
180
181 pub fn focus_type(&self) -> &str {
183 match self {
184 Self::LiveKit(_) => "livekit",
185 Self::_Custom(custom) => &custom.focus_type,
186 }
187 }
188
189 pub fn data(&self) -> Cow<'_, JsonObject> {
197 fn serialize<T: Serialize>(object: &T) -> JsonObject {
198 match serde_json::to_value(object).expect("rtc focus type serialization to succeed") {
199 JsonValue::Object(object) => object,
200 _ => panic!("all rtc focus types must serialize to objects"),
201 }
202 }
203
204 match self {
205 Self::LiveKit(info) => Cow::Owned(serialize(info)),
206 Self::_Custom(info) => Cow::Borrowed(&info.data),
207 }
208 }
209}
210
211#[cfg(feature = "unstable-msc4143")]
212impl<'de> Deserialize<'de> for RtcFocusInfo {
213 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
214 where
215 D: Deserializer<'de>,
216 {
217 #[derive(Deserialize)]
218 struct RtcFocusInfoDeHelper {
219 #[serde(rename = "type")]
220 focus_type: String,
221 }
222
223 let json = Box::<RawJsonValue>::deserialize(deserializer)?;
224 let RtcFocusInfoDeHelper { focus_type } = from_raw_json_value(&json)?;
225
226 Ok(match focus_type.as_ref() {
227 "livekit" => Self::LiveKit(from_raw_json_value(&json)?),
228 _ => {
229 let mut data = from_raw_json_value::<JsonObject, _>(&json)?;
230 data.remove("type");
231
232 Self::_Custom(CustomRtcFocusInfo { focus_type, data })
233 }
234 })
235 }
236}
237
238#[cfg(feature = "unstable-msc4143")]
240#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
241#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
242pub struct LiveKitRtcFocusInfo {
243 #[serde(rename = "livekit_service_url")]
245 pub service_url: String,
246}
247
248#[doc(hidden)]
250#[cfg(feature = "unstable-msc4143")]
251#[derive(Clone, Debug, Serialize, PartialEq, Eq)]
252pub struct CustomRtcFocusInfo {
253 #[serde(rename = "type")]
255 focus_type: String,
256
257 #[serde(flatten)]
259 data: JsonObject,
260}
261
262#[cfg(test)]
263mod tests {
264 #[cfg(feature = "unstable-msc4143")]
265 use assert_matches2::assert_let;
266 #[cfg(feature = "unstable-msc4143")]
267 use ruma_common::canonical_json::assert_to_canonical_json_eq;
268 #[cfg(feature = "unstable-msc4143")]
269 use serde_json::{from_value as from_json_value, json};
270
271 #[cfg(feature = "unstable-msc4143")]
272 use super::RtcFocusInfo;
273
274 #[test]
275 #[cfg(feature = "unstable-msc4143")]
276 fn test_livekit_rtc_focus_deserialization() {
277 let json = json!({
279 "type": "livekit",
280 "livekit_service_url": "https://livekit.example.com"
281 });
282
283 let focus: RtcFocusInfo = from_json_value(json).unwrap();
285
286 assert_let!(RtcFocusInfo::LiveKit(info) = focus);
288 assert_eq!(info.service_url, "https://livekit.example.com");
289 }
290
291 #[test]
292 #[cfg(feature = "unstable-msc4143")]
293 fn test_livekit_rtc_focus_serialization() {
294 let focus = RtcFocusInfo::livekit("https://livekit.example.com".to_owned());
296
297 assert_to_canonical_json_eq!(
299 focus,
300 json!({
301 "type": "livekit",
302 "livekit_service_url": "https://livekit.example.com"
303 })
304 );
305 }
306
307 #[test]
308 #[cfg(feature = "unstable-msc4143")]
309 fn test_custom_rtc_focus_serialization() {
310 let json = json!({
312 "type": "some-focus-type",
313 "additional-type-specific-field": "https://my_focus.domain",
314 "another-additional-type-specific-field": ["with", "Array", "type"]
315 });
316
317 let focus: RtcFocusInfo = from_json_value(json.clone()).unwrap();
319
320 assert_eq!(focus.focus_type(), "some-focus-type");
323
324 let data = &focus.data();
325 assert_eq!(data["additional-type-specific-field"], "https://my_focus.domain");
326
327 let array_values: Vec<&str> = data["another-additional-type-specific-field"]
328 .as_array()
329 .unwrap()
330 .iter()
331 .map(|v| v.as_str().unwrap())
332 .collect();
333 assert_eq!(array_values, vec!["with", "Array", "type"]);
334
335 assert!(!data.contains_key("type"));
336
337 assert_to_canonical_json_eq!(focus, json);
339 }
340}