1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
use std::{
    collections::HashMap,
    fmt::{self, Debug},
    marker::PhantomData,
    sync::Mutex,
};

use futures_util::StreamExt;
use serde::{
    de::{self, Error as SeError, Visitor},
    ser::SerializeTuple,
    Deserialize, Deserializer, Serialize,
};
use zbus::{
    proxy::SignalStream,
    zvariant::{ObjectPath, Type, Value},
};

use crate::{desktop::HandleToken, proxy::Proxy, Error};

/// A typical response returned by the [`Request::response`].
/// of a [`Request`].
#[derive(Debug, Type)]
#[zvariant(signature = "ua{sv}")]
pub enum Response<T>
where
    T: for<'de> Deserialize<'de> + Type,
{
    /// Success, the request is carried out.
    Ok(T),
    /// The user cancelled the request or something else happened.
    Err(ResponseError),
}

#[cfg(feature = "backend")]
impl<T> Response<T>
where
    T: for<'de> Deserialize<'de> + Type,
{
    pub fn ok(inner: T) -> Self {
        Self::Ok(inner)
    }

    pub fn cancelled() -> Self {
        Self::Err(ResponseError::Cancelled)
    }

    pub fn other() -> Self {
        Self::Err(ResponseError::Other)
    }
}

impl<'de, T> Deserialize<'de> for Response<T>
where
    T: for<'d> Deserialize<'d> + Type,
{
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        struct ResponseVisitor<T>(PhantomData<fn() -> (ResponseType, T)>);

        impl<'de, T> Visitor<'de> for ResponseVisitor<T>
        where
            T: Deserialize<'de>,
        {
            type Value = (ResponseType, Option<T>);

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(
                    formatter,
                    "a tuple composed of the response status along with the response"
                )
            }

            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
            where
                A: de::SeqAccess<'de>,
            {
                let type_: ResponseType = seq.next_element()?.ok_or_else(|| A::Error::custom(
                    "Failed to deserialize the response. Expected a numeric (u) value as the first item of the returned tuple",
                ))?;
                if type_ == ResponseType::Success {
                    let data: T = seq.next_element()?.ok_or_else(|| A::Error::custom(
                        "Failed to deserialize the response. Expected a vardict (a{sv}) with the returned results",
                    ))?;
                    Ok((type_, Some(data)))
                } else {
                    Ok((type_, None))
                }
            }
        }

        let visitor = ResponseVisitor::<T>(PhantomData);
        let response: (ResponseType, Option<T>) = deserializer.deserialize_tuple(2, visitor)?;
        Ok(response.into())
    }
}

impl<T> Serialize for Response<T>
where
    T: for<'de> Deserialize<'de> + Serialize + Type,
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        let mut map = serializer.serialize_tuple(2)?;
        match self {
            Self::Err(err) => {
                map.serialize_element(&ResponseType::from(*err))?;
                map.serialize_element(&HashMap::<&str, Value<'_>>::new())?;
            }
            Self::Ok(response) => {
                map.serialize_element(&ResponseType::Success)?;
                map.serialize_element(response)?;
            }
        };
        map.end()
    }
}

#[doc(hidden)]
impl<T> From<(ResponseType, Option<T>)> for Response<T>
where
    T: for<'de> Deserialize<'de> + Type,
{
    fn from(f: (ResponseType, Option<T>)) -> Self {
        match f.0 {
            ResponseType::Success => {
                Response::Ok(f.1.expect("Expected a valid response, found nothing."))
            }
            ResponseType::Cancelled => Response::Err(ResponseError::Cancelled),
            ResponseType::Other => Response::Err(ResponseError::Other),
        }
    }
}

#[derive(Debug, Copy, PartialEq, Eq, Hash, Clone)]
/// An error returned a portal request caused by either the user cancelling the
/// request or something else.
pub enum ResponseError {
    /// The user canceled the request.
    Cancelled,
    /// Something else happened.
    Other,
}

impl std::error::Error for ResponseError {}

impl std::fmt::Display for ResponseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::Cancelled => f.write_str("Cancelled"),
            Self::Other => f.write_str("Other"),
        }
    }
}

#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Type)]
#[doc(hidden)]
enum ResponseType {
    /// Success, the request is carried out.
    Success = 0,
    /// The user cancelled the interaction.
    Cancelled = 1,
    /// The user interaction was ended in some other way.
    Other = 2,
}

#[doc(hidden)]
impl From<ResponseError> for ResponseType {
    fn from(err: ResponseError) -> Self {
        match err {
            ResponseError::Other => Self::Other,
            ResponseError::Cancelled => Self::Cancelled,
        }
    }
}

/// The Request interface is shared by all portal interfaces.
/// When a portal method is called, the reply includes a handle (i.e. object
/// path) for a Request object, which will stay alive for the duration of the
/// user interaction related to the method call.
///
/// The portal indicates that a portal request interaction is over by emitting
/// the "Response" signal on the Request object.
///
/// The application can abort the interaction calling
/// [`close()`][`Request::close`] on the Request object.
///
/// Wrapper of the DBus interface: [`org.freedesktop.portal.Request`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html).
#[doc(alias = "org.freedesktop.portal.Request")]
pub struct Request<T>(
    Proxy<'static>,
    SignalStream<'static>,
    Mutex<Option<Result<T, Error>>>,
    PhantomData<T>,
)
where
    T: for<'de> Deserialize<'de> + Type + Debug;

impl<T> Request<T>
where
    T: for<'de> Deserialize<'de> + Type + Debug,
{
    pub(crate) async fn new<P>(path: P) -> Result<Request<T>, Error>
    where
        P: TryInto<ObjectPath<'static>>,
        P::Error: Into<zbus::Error>,
    {
        let proxy = Proxy::new_desktop_with_path("org.freedesktop.portal.Request", path).await?;
        // Start listening for a response signal the moment request is created
        let stream = proxy.receive_signal("Response").await?;
        Ok(Self(proxy, stream, Default::default(), PhantomData))
    }

    pub(crate) async fn from_unique_name(handle_token: &HandleToken) -> Result<Request<T>, Error> {
        let path =
            Proxy::unique_name("/org/freedesktop/portal/desktop/request", handle_token).await?;
        #[cfg(feature = "tracing")]
        tracing::info!("Creating a org.freedesktop.portal.Request {}", path);
        Self::new(path).await
    }

    pub(crate) async fn prepare_response(&mut self) -> Result<(), Error> {
        let message = self.1.next().await.ok_or(Error::NoResponse)?;
        #[cfg(feature = "tracing")]
        tracing::info!("Received signal 'Response' on '{}'", self.0.interface());
        let response = match message.body().deserialize::<Response<T>>()? {
            Response::Err(e) => Err(e.into()),
            Response::Ok(r) => Ok(r),
        };
        #[cfg(feature = "tracing")]
        tracing::debug!("Received response {:#?}", response);
        let r = response as Result<T, Error>;
        *self.2.get_mut().unwrap() = Some(r);
        Ok(())
    }

    /// The corresponding response if the request was successful.
    ///
    /// # Specifications
    ///
    /// See also [`Response`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html#org-freedesktop-portal-request-response).
    pub fn response(&self) -> Result<T, Error> {
        // It should be safe to unwrap here as we are sure we have received a response
        // by the time the user calls response
        self.2.lock().unwrap().take().unwrap()
    }

    /// Closes the portal request to which this object refers and ends all
    /// related user interaction (dialogs, etc). A Response signal will not
    /// be emitted in this case.
    ///
    /// # Specifications
    ///
    /// See also [`Close`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Request.html#org-freedesktop-portal-request-close).
    #[doc(alias = "Close")]
    pub async fn close(&self) -> Result<(), Error> {
        self.0.call("Close", &()).await
    }

    pub(crate) fn path(&self) -> &ObjectPath<'_> {
        self.0.path()
    }
}

impl<T> Debug for Request<T>
where
    T: for<'de> Deserialize<'de> + Type + Debug,
{
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_tuple("Request")
            .field(&self.path().as_str())
            .finish()
    }
}