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 ruma_common::canonical_json::assert_to_canonical_json_eq;
239 #[cfg(feature = "unstable-msc4143")]
240 use serde_json::{from_value as from_json_value, json};
241
242 #[cfg(feature = "unstable-msc4143")]
243 use super::RtcFocusInfo;
244
245 #[test]
246 #[cfg(feature = "unstable-msc4143")]
247 fn test_livekit_rtc_focus_deserialization() {
248 let json = json!({
250 "type": "livekit",
251 "livekit_service_url": "https://livekit.example.com"
252 });
253
254 let focus: RtcFocusInfo = from_json_value(json).unwrap();
256
257 assert_matches!(focus, RtcFocusInfo::LiveKit(info));
259 assert_eq!(info.service_url, "https://livekit.example.com");
260 }
261
262 #[test]
263 #[cfg(feature = "unstable-msc4143")]
264 fn test_livekit_rtc_focus_serialization() {
265 let focus = RtcFocusInfo::livekit("https://livekit.example.com".to_owned());
267
268 assert_to_canonical_json_eq!(
270 focus,
271 json!({
272 "type": "livekit",
273 "livekit_service_url": "https://livekit.example.com"
274 })
275 );
276 }
277
278 #[test]
279 #[cfg(feature = "unstable-msc4143")]
280 fn test_custom_rtc_focus_serialization() {
281 let json = json!({
283 "type": "some-focus-type",
284 "additional-type-specific-field": "https://my_focus.domain",
285 "another-additional-type-specific-field": ["with", "Array", "type"]
286 });
287
288 let focus: RtcFocusInfo = from_json_value(json.clone()).unwrap();
290
291 assert_eq!(focus.focus_type(), "some-focus-type");
294
295 let data = &focus.data();
296 assert_eq!(data["additional-type-specific-field"], "https://my_focus.domain");
297
298 let array_values: Vec<&str> = data["another-additional-type-specific-field"]
299 .as_array()
300 .unwrap()
301 .iter()
302 .map(|v| v.as_str().unwrap())
303 .collect();
304 assert_eq!(array_values, vec!["with", "Array", "type"]);
305
306 assert!(!data.contains_key("type"));
307
308 assert_to_canonical_json_eq!(focus, json);
310 }
311}