use std::{collections::HashMap, fmt::Debug, marker::PhantomData};
use futures_util::Stream;
use serde::{Deserialize, Serialize, Serializer};
use zbus::zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Type, as_value};
use crate::{Error, desktop::HandleToken, proxy::Proxy};
#[derive(Type)]
#[doc(alias = "org.freedesktop.portal.Session")]
#[zvariant(signature = "o")]
pub struct Session<T>(Proxy<'static>, PhantomData<T>)
where
T: SessionPortal;
impl<T> Session<T>
where
T: SessionPortal,
{
pub(crate) async fn with_connection<P>(
connection: zbus::Connection,
path: P,
) -> Result<Self, Error>
where
P: TryInto<ObjectPath<'static>>,
P::Error: Into<zbus::Error>,
{
let proxy =
Proxy::new_desktop_with_path(connection, "org.freedesktop.portal.Session", path)
.await?;
Ok(Self(proxy, PhantomData))
}
pub(crate) async fn from_unique_name(
connection: zbus::Connection,
handle_token: &HandleToken,
) -> Result<Self, crate::Error> {
let path = Proxy::unique_name(
&connection,
"/org/freedesktop/portal/desktop/session",
handle_token,
)
.await?;
#[cfg(feature = "tracing")]
tracing::info!("Creating a org.freedesktop.portal.Session {}", path);
Self::with_connection(connection, path).await
}
#[doc(alias = "Closed")]
pub async fn receive_closed(
&self,
) -> Result<impl Stream<Item = HashMap<String, OwnedValue>>, Error> {
self.0.signal("Closed").await
}
#[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> Serialize for Session<T>
where
T: SessionPortal,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
ObjectPath::serialize(self.path(), serializer)
}
}
impl<T> Debug for Session<T>
where
T: SessionPortal,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Session")
.field(&self.path().as_str())
.finish()
}
}
pub trait SessionPortal: crate::Sealed {}
#[derive(Type, Debug)]
#[zvariant(signature = "dict")]
pub(crate) struct CreateSessionResponse {
pub(crate) session_handle: OwnedObjectPath,
}
impl<'de> Deserialize<'de> for CreateSessionResponse {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let map: HashMap<String, OwnedValue> = HashMap::deserialize(deserializer)?;
let session_handle = map.get("session_handle").ok_or_else(|| {
serde::de::Error::custom(
"CreateSessionResponse failed to deserialize. Couldn't find a session_handle",
)
})?;
let path = if let Ok(object_path_str) = session_handle.downcast_ref::<&str>() {
ObjectPath::try_from(object_path_str).unwrap()
} else if let Ok(object_path) = session_handle.downcast_ref::<ObjectPath<'_>>() {
object_path
} else {
return Err(serde::de::Error::custom(
"Wrong session_handle type. Expected `s` or `o`.",
));
};
Ok(Self {
session_handle: path.into(),
})
}
}
#[derive(Serialize, Deserialize, Type, Debug, Default)]
#[zvariant(signature = "dict")]
pub struct CreateSessionOptions {
#[serde(default, with = "as_value", skip_deserializing)]
pub(crate) handle_token: HandleToken,
#[serde(default, with = "as_value", skip_deserializing)]
pub(crate) session_handle_token: HandleToken,
}