1use std::{
2 collections::HashMap,
3 fmt::{self, Debug},
4 marker::PhantomData,
5 sync::Mutex,
6};
7
8use futures_util::StreamExt;
9use serde::{
10 de::{self, Error as SeError, Visitor},
11 ser::SerializeTuple,
12 Deserialize, Deserializer, Serialize,
13};
14use zbus::{
15 proxy::SignalStream,
16 zvariant::{ObjectPath, Type, Value},
17};
18
19use crate::{desktop::HandleToken, proxy::Proxy, Error};
20
21#[derive(Debug, Type)]
24#[zvariant(signature = "(ua{sv})")]
25pub enum Response<T> {
26 Ok(T),
28 Err(ResponseError),
30}
31
32#[cfg(feature = "backend")]
33#[cfg_attr(docsrs, doc(cfg(feature = "backend")))]
34impl<T> Response<T> {
35 pub fn response_type(self) -> ResponseType {
37 match self {
38 Self::Ok(_) => ResponseType::Success,
39 Self::Err(err) => match err {
40 ResponseError::Cancelled => ResponseType::Cancelled,
41 ResponseError::Other => ResponseType::Other,
42 },
43 }
44 }
45
46 pub fn ok(inner: T) -> Self {
48 Self::Ok(inner)
49 }
50
51 pub fn cancelled() -> Self {
53 Self::Err(ResponseError::Cancelled)
54 }
55
56 pub fn other() -> Self {
58 Self::Err(ResponseError::Other)
59 }
60}
61
62impl<'de, T> Deserialize<'de> for Response<T>
63where
64 T: for<'d> Deserialize<'d> + Type,
65{
66 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67 where
68 D: Deserializer<'de>,
69 {
70 struct ResponseVisitor<T>(PhantomData<fn() -> (ResponseType, T)>);
71
72 impl<'de, T> Visitor<'de> for ResponseVisitor<T>
73 where
74 T: Deserialize<'de>,
75 {
76 type Value = (ResponseType, Option<T>);
77
78 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
79 write!(
80 formatter,
81 "a tuple composed of the response status along with the response"
82 )
83 }
84
85 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
86 where
87 A: de::SeqAccess<'de>,
88 {
89 let type_: ResponseType = seq.next_element()?.ok_or_else(|| A::Error::custom(
90 "Failed to deserialize the response. Expected a numeric (u) value as the first item of the returned tuple",
91 ))?;
92 if type_ == ResponseType::Success {
93 let data: T = seq.next_element()?.ok_or_else(|| A::Error::custom(
94 "Failed to deserialize the response. Expected a vardict (a{sv}) with the returned results",
95 ))?;
96 Ok((type_, Some(data)))
97 } else {
98 Ok((type_, None))
99 }
100 }
101 }
102
103 let visitor = ResponseVisitor::<T>(PhantomData);
104 let response: (ResponseType, Option<T>) = deserializer.deserialize_tuple(2, visitor)?;
105 Ok(response.into())
106 }
107}
108
109impl<T> Serialize for Response<T>
110where
111 T: Serialize + Type,
112{
113 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
114 where
115 S: serde::Serializer,
116 {
117 let mut map = serializer.serialize_tuple(2)?;
118 match self {
119 Self::Err(err) => {
120 map.serialize_element(&ResponseType::from(*err))?;
121 map.serialize_element(&HashMap::<&str, Value<'_>>::new())?;
122 }
123 Self::Ok(response) => {
124 map.serialize_element(&ResponseType::Success)?;
125 map.serialize_element(response)?;
126 }
127 };
128 map.end()
129 }
130}
131
132#[doc(hidden)]
133impl<T> From<(ResponseType, Option<T>)> for Response<T> {
134 fn from(f: (ResponseType, Option<T>)) -> Self {
135 match f.0 {
136 ResponseType::Success => {
137 Response::Ok(f.1.expect("Expected a valid response, found nothing."))
138 }
139 ResponseType::Cancelled => Response::Err(ResponseError::Cancelled),
140 ResponseType::Other => Response::Err(ResponseError::Other),
141 }
142 }
143}
144
145#[derive(Debug, Copy, PartialEq, Eq, Hash, Clone)]
146pub enum ResponseError {
149 Cancelled,
151 Other,
153}
154
155impl std::error::Error for ResponseError {}
156
157impl std::fmt::Display for ResponseError {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 match self {
160 Self::Cancelled => f.write_str("Cancelled"),
161 Self::Other => f.write_str("Other"),
162 }
163 }
164}
165
166#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Type)]
167pub enum ResponseType {
169 Success = 0,
171 Cancelled = 1,
173 Other = 2,
175}
176
177#[doc(hidden)]
178impl From<ResponseError> for ResponseType {
179 fn from(err: ResponseError) -> Self {
180 match err {
181 ResponseError::Other => Self::Other,
182 ResponseError::Cancelled => Self::Cancelled,
183 }
184 }
185}
186
187#[doc(alias = "org.freedesktop.portal.Request")]
201pub struct Request<T>(
202 Proxy<'static>,
203 SignalStream<'static>,
204 Mutex<Option<Result<T, Error>>>,
205 PhantomData<T>,
206)
207where
208 T: for<'de> Deserialize<'de> + Type + Debug;
209
210impl<T> Request<T>
211where
212 T: for<'de> Deserialize<'de> + Type + Debug,
213{
214 pub(crate) async fn new<P>(path: P) -> Result<Request<T>, Error>
215 where
216 P: TryInto<ObjectPath<'static>>,
217 P::Error: Into<zbus::Error>,
218 {
219 let proxy = Proxy::new_desktop_with_path("org.freedesktop.portal.Request", path).await?;
220 let stream = proxy.receive_signal("Response").await?;
222 Ok(Self(proxy, stream, Default::default(), PhantomData))
223 }
224
225 pub(crate) async fn from_unique_name(handle_token: &HandleToken) -> Result<Request<T>, Error> {
226 let path =
227 Proxy::unique_name("/org/freedesktop/portal/desktop/request", handle_token).await?;
228 #[cfg(feature = "tracing")]
229 tracing::info!("Creating a org.freedesktop.portal.Request {}", path);
230 Self::new(path).await
231 }
232
233 pub(crate) async fn prepare_response(&mut self) -> Result<(), Error> {
234 let message = self.1.next().await.ok_or(Error::NoResponse)?;
235 #[cfg(feature = "tracing")]
236 tracing::info!("Received signal 'Response' on '{}'", self.0.interface());
237 let response = match message.body().deserialize::<Response<T>>()? {
238 Response::Err(e) => Err(e.into()),
239 Response::Ok(r) => Ok(r),
240 };
241 #[cfg(feature = "tracing")]
242 tracing::debug!("Received response {:#?}", response);
243 let r = response as Result<T, Error>;
244 *self.2.get_mut().unwrap() = Some(r);
245 Ok(())
246 }
247
248 pub fn response(&self) -> Result<T, Error> {
254 self.2.lock().unwrap().take().unwrap()
257 }
258
259 #[doc(alias = "Close")]
267 pub async fn close(&self) -> Result<(), Error> {
268 self.0.call("Close", &()).await
269 }
270
271 pub(crate) fn path(&self) -> &ObjectPath<'_> {
273 self.0.path()
274 }
275}
276
277impl<T> Debug for Request<T>
278where
279 T: for<'de> Deserialize<'de> + Type + Debug,
280{
281 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 f.debug_tuple("Request")
283 .field(&self.path().as_str())
284 .finish()
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use zbus::zvariant::Value;
291
292 use super::*;
293
294 #[test]
295 fn response_signature() {
296 use crate::desktop::account::UserInformation;
297 assert_eq!(
298 <(ResponseType, HashMap<&str, Value<'_>>)>::SIGNATURE,
299 Response::<()>::SIGNATURE,
300 );
301 assert_eq!(
302 <(ResponseType, UserInformation)>::SIGNATURE,
303 Response::<UserInformation>::SIGNATURE,
304 );
305 assert_eq!(Response::<()>::SIGNATURE, "(ua{sv})");
306 }
307}