use super::DESTINATION;
use crate::{
desktop::HandleToken,
helpers::{call_method, receive_signal},
Error,
};
use serde::{
de::{self, Error as SeError, Visitor},
Deserialize, Deserializer, Serialize,
};
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::{
collections::HashMap,
convert::TryFrom,
fmt::{self, Debug},
marker::PhantomData,
};
use zvariant::OwnedValue;
use zvariant_derive::Type;
#[derive(Debug)]
enum Response<T>
where
T: for<'de> Deserialize<'de> + zvariant::Type,
{
Ok(T),
Err(ResponseError),
}
impl<T> zvariant::Type for Response<T>
where
T: for<'de> Deserialize<'de> + zvariant::Type,
{
fn signature() -> zvariant::Signature<'static> {
<(ResponseType, HashMap<&str, OwnedValue>)>::signature()
}
}
impl<'de, T> Deserialize<'de> for Response<T>
where
T: for<'d> Deserialize<'d> + zvariant::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())
}
}
#[doc(hidden)]
impl<T> From<(ResponseType, Option<T>)> for Response<T>
where
T: for<'de> Deserialize<'de> + zvariant::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, Serialize, Deserialize, Type)]
pub(crate) struct BasicResponse(HashMap<String, OwnedValue>);
#[derive(Debug, Copy, PartialEq, Hash, Clone)]
pub enum ResponseError {
Cancelled,
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_repr, Deserialize_repr, PartialEq, Debug, Type)]
#[repr(u32)]
#[doc(hidden)]
enum ResponseType {
Success = 0,
Cancelled = 1,
Other = 2,
}
#[doc(hidden)]
impl From<ResponseError> for ResponseType {
fn from(err: ResponseError) -> Self {
match err {
ResponseError::Other => Self::Other,
ResponseError::Cancelled => Self::Cancelled,
}
}
}
#[derive(Debug)]
#[doc(alias = "org.freedesktop.portal.Request")]
pub(crate) struct RequestProxy<'a>(zbus::azync::Proxy<'a>);
impl<'a> RequestProxy<'a> {
pub async fn new(
connection: &zbus::azync::Connection,
path: zvariant::ObjectPath<'a>,
) -> Result<RequestProxy<'a>, Error> {
let proxy = zbus::ProxyBuilder::new_bare(connection)
.interface("org.freedesktop.portal.Request")
.path(path)?
.destination(DESTINATION)
.build_async()
.await?;
Ok(Self(proxy))
}
pub async fn from_unique_name(
connection: &zbus::azync::Connection,
handle_token: &HandleToken,
) -> Result<RequestProxy<'a>, Error> {
let unique_name = connection.unique_name().unwrap();
let unique_identifier = unique_name.trim_start_matches(':').replace('.', "_");
let path = zvariant::ObjectPath::try_from(format!(
"/org/freedesktop/portal/desktop/request/{}/{}",
unique_identifier, handle_token
))
.unwrap();
RequestProxy::new(connection, path).await
}
pub fn inner(&self) -> &zbus::azync::Proxy<'_> {
&self.0
}
#[doc(alias = "Response")]
pub async fn receive_response<R>(&self) -> Result<R, Error>
where
R: for<'de> Deserialize<'de> + zvariant::Type,
{
match receive_signal::<Response<R>>(&self.0, "Response").await? {
Response::Err(e) => Err(e.into()),
Response::Ok(r) => Ok(r),
}
}
#[allow(dead_code)]
#[doc(alias = "Close")]
pub async fn close(&self) -> Result<(), Error> {
call_method(&self.0, "Close", &()).await
}
}