ruma_client_api/profile/
get_profile_field.rs1pub mod v3 {
6 use std::marker::PhantomData;
11
12 use ruma_common::{
13 api::{request, Metadata},
14 metadata, OwnedUserId,
15 };
16
17 use crate::profile::{
18 profile_field_serde::StaticProfileFieldVisitor, ProfileFieldName, ProfileFieldValue,
19 StaticProfileField,
20 };
21
22 const METADATA: Metadata = metadata! {
23 method: GET,
24 rate_limited: false,
25 authentication: None,
26 history: {
27 unstable("uk.tcpip.msc4133") => "/_matrix/client/unstable/uk.tcpip.msc4133/profile/{user_id}/{field}",
28 }
30 };
31
32 #[request(error = crate::Error)]
34 pub struct Request {
35 #[ruma_api(path)]
37 pub user_id: OwnedUserId,
38
39 #[ruma_api(path)]
41 pub field: ProfileFieldName,
42 }
43
44 impl Request {
45 pub fn new(user_id: OwnedUserId, field: ProfileFieldName) -> Self {
47 Self { user_id, field }
48 }
49
50 pub fn new_static<F: StaticProfileField>(user_id: OwnedUserId) -> RequestStatic<F> {
52 RequestStatic::new(user_id)
53 }
54 }
55
56 #[derive(Debug)]
58 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
59 pub struct RequestStatic<F: StaticProfileField> {
60 pub user_id: OwnedUserId,
62
63 field: PhantomData<F>,
65 }
66
67 impl<F: StaticProfileField> RequestStatic<F> {
68 pub fn new(user_id: OwnedUserId) -> Self {
70 Self { user_id, field: PhantomData }
71 }
72 }
73
74 impl<F: StaticProfileField> Clone for RequestStatic<F> {
75 fn clone(&self) -> Self {
76 Self { user_id: self.user_id.clone(), field: self.field }
77 }
78 }
79
80 #[cfg(feature = "client")]
81 impl<F: StaticProfileField> ruma_common::api::OutgoingRequest for RequestStatic<F> {
82 type EndpointError = crate::Error;
83 type IncomingResponse = ResponseStatic<F>;
84
85 const METADATA: Metadata = METADATA;
86
87 fn try_into_http_request<T: Default + bytes::BufMut>(
88 self,
89 base_url: &str,
90 access_token: ruma_common::api::SendAccessToken<'_>,
91 considering: &'_ ruma_common::api::SupportedVersions,
92 ) -> Result<http::Request<T>, ruma_common::api::error::IntoHttpError> {
93 Request::new(self.user_id, F::NAME.into()).try_into_http_request(
94 base_url,
95 access_token,
96 considering,
97 )
98 }
99 }
100
101 #[derive(Debug, Clone, Default)]
103 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
104 pub struct Response {
105 pub value: Option<ProfileFieldValue>,
107 }
108
109 impl Response {
110 pub fn new(value: ProfileFieldValue) -> Self {
112 Self { value: Some(value) }
113 }
114 }
115
116 #[cfg(feature = "client")]
117 impl ruma_common::api::IncomingResponse for Response {
118 type EndpointError = crate::Error;
119
120 fn try_from_http_response<T: AsRef<[u8]>>(
121 response: http::Response<T>,
122 ) -> Result<Self, ruma_common::api::error::FromHttpResponseError<Self::EndpointError>>
123 {
124 use ruma_common::api::EndpointError;
125
126 use crate::profile::profile_field_serde::deserialize_profile_field_value_option;
127
128 if response.status().as_u16() >= 400 {
129 return Err(ruma_common::api::error::FromHttpResponseError::Server(
130 Self::EndpointError::from_http_response(response),
131 ));
132 }
133
134 let mut de = serde_json::Deserializer::from_slice(response.body().as_ref());
135 let value = deserialize_profile_field_value_option(&mut de)?;
136 de.end()?;
137
138 Ok(Self { value })
139 }
140 }
141
142 #[cfg(feature = "server")]
143 impl ruma_common::api::OutgoingResponse for Response {
144 fn try_into_http_response<T: Default + bytes::BufMut>(
145 self,
146 ) -> Result<http::Response<T>, ruma_common::api::error::IntoHttpError> {
147 use ruma_common::serde::JsonObject;
148
149 let body = self
150 .value
151 .as_ref()
152 .map(|value| ruma_common::serde::json_to_buf(value))
153 .unwrap_or_else(||
154 ruma_common::serde::json_to_buf(&JsonObject::new()))?;
156
157 Ok(http::Response::builder()
158 .status(http::StatusCode::OK)
159 .header(http::header::CONTENT_TYPE, "application/json")
160 .body(body)?)
161 }
162 }
163
164 #[derive(Debug)]
166 #[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
167 pub struct ResponseStatic<F: StaticProfileField> {
168 pub value: Option<F::Value>,
170 }
171
172 impl<F: StaticProfileField> Clone for ResponseStatic<F>
173 where
174 F::Value: Clone,
175 {
176 fn clone(&self) -> Self {
177 Self { value: self.value.clone() }
178 }
179 }
180
181 #[cfg(feature = "client")]
182 impl<F: StaticProfileField> ruma_common::api::IncomingResponse for ResponseStatic<F> {
183 type EndpointError = crate::Error;
184
185 fn try_from_http_response<T: AsRef<[u8]>>(
186 response: http::Response<T>,
187 ) -> Result<Self, ruma_common::api::error::FromHttpResponseError<Self::EndpointError>>
188 {
189 use ruma_common::api::EndpointError;
190 use serde::de::Deserializer;
191
192 if response.status().as_u16() >= 400 {
193 return Err(ruma_common::api::error::FromHttpResponseError::Server(
194 Self::EndpointError::from_http_response(response),
195 ));
196 }
197
198 let value = serde_json::Deserializer::from_slice(response.into_body().as_ref())
199 .deserialize_map(StaticProfileFieldVisitor(PhantomData::<F>))?;
200
201 Ok(Self { value })
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use ruma_common::{owned_mxc_uri, owned_user_id};
209 use serde_json::{
210 from_slice as from_json_slice, json, to_vec as to_json_vec, Value as JsonValue,
211 };
212
213 use super::v3::{Request, RequestStatic, Response};
214 use crate::profile::{ProfileFieldName, ProfileFieldValue};
215
216 #[test]
217 #[cfg(feature = "client")]
218 fn serialize_request() {
219 use ruma_common::api::{OutgoingRequest, SendAccessToken, SupportedVersions};
220
221 let request = Request::new(owned_user_id!("@alice:localhost"), ProfileFieldName::AvatarUrl);
222
223 let http_request = request
224 .try_into_http_request::<Vec<u8>>(
225 "http://localhost/",
226 SendAccessToken::Always("access_token"),
227 &SupportedVersions::from_parts(&["v11".to_owned()], &Default::default()),
228 )
229 .unwrap();
230
231 assert_eq!(
232 http_request.uri().path(),
233 "/_matrix/client/unstable/uk.tcpip.msc4133/profile/@alice:localhost/avatar_url"
234 );
235 }
236
237 #[test]
238 #[cfg(feature = "server")]
239 fn deserialize_request() {
240 use ruma_common::api::IncomingRequest;
241
242 let request = Request::try_from_http_request(
243 http::Request::get("http://localhost/_matrix/client/unstable/uk.tcpip.msc4133/profile/@alice:localhost/displayname").body(Vec::<u8>::new()).unwrap(),
244 &["@alice:localhost", "displayname"],
245 ).unwrap();
246
247 assert_eq!(request.user_id, "@alice:localhost");
248 assert_eq!(request.field, ProfileFieldName::DisplayName);
249 }
250
251 #[test]
252 #[cfg(feature = "server")]
253 fn serialize_response() {
254 use ruma_common::api::OutgoingResponse;
255
256 let response =
257 Response::new(ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef")));
258
259 let http_response = response.try_into_http_response::<Vec<u8>>().unwrap();
260
261 assert_eq!(
262 from_json_slice::<JsonValue>(http_response.body().as_ref()).unwrap(),
263 json!({
264 "avatar_url": "mxc://localhost/abcdef",
265 })
266 );
267 }
268
269 #[test]
270 #[cfg(feature = "client")]
271 fn deserialize_response() {
272 use ruma_common::api::IncomingResponse;
273
274 let body = to_json_vec(&json!({
275 "custom_field": "value",
276 }))
277 .unwrap();
278
279 let response = Response::try_from_http_response(http::Response::new(body)).unwrap();
280 let value = response.value.unwrap();
281 assert_eq!(value.field_name().as_str(), "custom_field");
282 assert_eq!(value.value().as_str().unwrap(), "value");
283
284 let empty_body = to_json_vec(&json!({})).unwrap();
285
286 let response = Response::try_from_http_response(http::Response::new(empty_body)).unwrap();
287 assert!(response.value.is_none());
288 }
289
290 #[cfg(feature = "client")]
293 fn get_static_response<R: ruma_common::api::OutgoingRequest>(
294 value: Option<ProfileFieldValue>,
295 ) -> Result<R::IncomingResponse, ruma_common::api::error::FromHttpResponseError<R::EndpointError>>
296 {
297 use ruma_common::api::IncomingResponse;
298
299 let body =
300 value.map(|value| to_json_vec(&value).unwrap()).unwrap_or_else(|| b"{}".to_vec());
301 R::IncomingResponse::try_from_http_response(http::Response::new(body))
302 }
303
304 #[test]
305 #[cfg(feature = "client")]
306 fn static_request_and_valid_response() {
307 use crate::profile::AvatarUrl;
308
309 let response = get_static_response::<RequestStatic<AvatarUrl>>(Some(
310 ProfileFieldValue::AvatarUrl(owned_mxc_uri!("mxc://localhost/abcdef")),
311 ))
312 .unwrap();
313 assert_eq!(response.value.unwrap(), "mxc://localhost/abcdef");
314
315 let response = get_static_response::<RequestStatic<AvatarUrl>>(None).unwrap();
316 assert!(response.value.is_none());
317 }
318
319 #[test]
320 #[cfg(feature = "client")]
321 fn static_request_and_invalid_response() {
322 use crate::profile::AvatarUrl;
323
324 get_static_response::<RequestStatic<AvatarUrl>>(Some(ProfileFieldValue::DisplayName(
325 "Alice".to_owned(),
326 )))
327 .unwrap_err();
328 }
329}