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::{request, response, Metadata},
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
22const METADATA: Metadata = metadata! {
23 method: GET,
24 rate_limited: false,
25 authentication: None,
26 history: {
27 1.0 => "/.well-known/matrix/client",
28 }
29};
30
31#[request(error = crate::Error)]
33#[derive(Default)]
34pub struct Request {}
35
36#[response(error = crate::Error)]
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, Deserialize, 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")]
213#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
214#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
215pub struct LiveKitRtcFocusInfo {
216 #[serde(rename = "livekit_service_url")]
218 pub service_url: String,
219}
220
221#[doc(hidden)]
223#[cfg(feature = "unstable-msc4143")]
224#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)]
225pub struct CustomRtcFocusInfo {
226 #[serde(rename = "type")]
228 focus_type: String,
229
230 #[serde(flatten)]
232 data: JsonObject,
233}
234
235#[cfg(test)]
236mod tests {
237 #[cfg(feature = "unstable-msc4143")]
238 use assert_matches2::assert_matches;
239 #[cfg(feature = "unstable-msc4143")]
240 use serde_json::{from_value as from_json_value, json, to_value as to_json_value};
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 let json = to_json_value(&focus).unwrap();
270
271 assert_eq!(
273 json,
274 json!({
275 "type": "livekit",
276 "livekit_service_url": "https://livekit.example.com"
277 })
278 );
279 }
280
281 #[test]
282 #[cfg(feature = "unstable-msc4143")]
283 fn test_custom_rtc_focus_serialization() {
284 let json = json!({
286 "type": "some-focus-type",
287 "additional-type-specific-field": "https://my_focus.domain",
288 "another-additional-type-specific-field": ["with", "Array", "type"]
289 });
290
291 let focus: RtcFocusInfo = from_json_value(json.clone()).unwrap();
293
294 assert_eq!(focus.focus_type(), "some-focus-type");
297
298 let data = &focus.data();
299 assert_eq!(data["additional-type-specific-field"], "https://my_focus.domain");
300
301 let array_values: Vec<&str> = data["another-additional-type-specific-field"]
302 .as_array()
303 .unwrap()
304 .iter()
305 .map(|v| v.as_str().unwrap())
306 .collect();
307 assert_eq!(array_values, vec!["with", "Array", "type"]);
308
309 assert!(!data.contains_key("type"));
310
311 let serialized = to_json_value(&focus).unwrap();
313
314 assert_eq!(serialized, json);
316 }
317}