use async_io::block_on;
use std::{
convert::{TryFrom, TryInto},
future::ready,
};
use zvariant::{ObjectPath, OwnedValue, Value};
use crate::{
azync::{self, SignalHandlerId},
Connection, Error, Message, Result,
};
use crate::fdo;
#[derive(Debug)]
pub struct Proxy<'a> {
conn: Connection,
azync: azync::Proxy<'a>,
}
impl<'a> Proxy<'a> {
pub fn new<E>(
conn: &Connection,
destination: &'a str,
path: impl TryInto<ObjectPath<'a>, Error = E>,
interface: &'a str,
) -> Result<Self>
where
Error: From<E>,
{
let proxy = azync::Proxy::new(conn.inner(), destination, path, interface)?;
Ok(Self {
conn: conn.clone(),
azync: proxy,
})
}
pub fn new_owned<E>(
conn: Connection,
destination: String,
path: impl TryInto<ObjectPath<'static>, Error = E>,
interface: String,
) -> Result<Self>
where
Error: From<E>,
{
let proxy =
azync::Proxy::new_owned(conn.clone().into_inner(), destination, path, interface)?;
Ok(Self { conn, azync: proxy })
}
pub fn connection(&self) -> &Connection {
&self.conn
}
pub fn destination(&self) -> &str {
self.azync.destination()
}
pub fn path(&self) -> &ObjectPath<'_> {
self.azync.path()
}
pub fn interface(&self) -> &str {
self.azync.interface()
}
pub fn introspect(&self) -> fdo::Result<String> {
block_on(self.azync.introspect())
}
pub fn get_property<T>(&self, property_name: &str) -> fdo::Result<T>
where
T: TryFrom<OwnedValue>,
{
block_on(self.azync.get_property(property_name))
}
pub fn set_property<'t, T: 't>(&self, property_name: &str, value: T) -> fdo::Result<()>
where
T: Into<Value<'t>>,
{
block_on(self.azync.set_property(property_name, value))
}
pub fn call_method<B>(&self, method_name: &str, body: &B) -> Result<Message>
where
B: serde::ser::Serialize + zvariant::Type,
{
block_on(self.azync.call_method(method_name, body))
}
pub fn call<B, R>(&self, method_name: &str, body: &B) -> Result<R>
where
B: serde::ser::Serialize + zvariant::Type,
R: serde::de::DeserializeOwned + zvariant::Type,
{
block_on(self.azync.call(method_name, body))
}
pub fn connect_signal<H>(
&self,
signal_name: &'static str,
mut handler: H,
) -> fdo::Result<SignalHandlerId>
where
H: FnMut(&Message) -> Result<()> + Send + 'static,
{
block_on(
self.azync
.connect_signal(signal_name, move |msg| Box::pin(ready(handler(msg)))),
)
}
pub fn disconnect_signal(&self, handler_id: SignalHandlerId) -> fdo::Result<bool> {
block_on(self.azync.disconnect_signal(handler_id))
}
pub fn next_signal(&self) -> Result<Option<Message>> {
block_on(self.azync.next_signal())
}
pub fn handle_signal(&self, msg: &Message) -> Result<bool> {
block_on(self.azync.handle_signal(msg))
}
pub fn inner(&self) -> &azync::Proxy<'_> {
&self.azync
}
pub fn into_inner(self) -> azync::Proxy<'a> {
self.azync
}
pub(crate) fn has_signal_handler(&self, signal_name: &str) -> bool {
block_on(self.azync.has_signal_handler(signal_name))
}
pub(crate) fn resolve_name(&self) -> Result<()> {
block_on(self.azync.resolve_name())
}
pub(crate) fn destination_unique_name(&self) -> Option<&str> {
self.azync.destination_unique_name()
}
}
impl<'asref, 'p: 'asref> std::convert::AsRef<Proxy<'asref>> for Proxy<'p> {
fn as_ref(&self) -> &Proxy<'asref> {
&self
}
}
impl<'p, 'a: 'p> From<azync::Proxy<'a>> for Proxy<'p> {
fn from(proxy: azync::Proxy<'a>) -> Self {
Self {
conn: proxy.connection().clone().into(),
azync: proxy,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::{Arc, Mutex};
#[test]
fn signal() {
let conn = Connection::new_session().unwrap();
let owner_change_signaled = Arc::new(Mutex::new(false));
let name_acquired_signaled = Arc::new(Mutex::new(false));
let proxy = Proxy::new(
&conn,
"org.freedesktop.DBus",
"/org/freedesktop/DBus",
"org.freedesktop.DBus",
)
.unwrap();
let well_known = "org.freedesktop.zbus.ProxySignalTest";
let unique_name = conn.unique_name().unwrap().to_string();
{
let well_known = well_known.clone();
let signaled = owner_change_signaled.clone();
proxy
.connect_signal("NameOwnerChanged", move |m| {
let (name, _, new_owner) = m.body::<(&str, &str, &str)>()?;
if name != well_known {
return Ok(());
}
assert_eq!(new_owner, unique_name);
*signaled.lock().unwrap() = true;
Ok(())
})
.unwrap();
}
{
let signaled = name_acquired_signaled.clone();
proxy
.connect_signal("NameAcquired", move |m| {
if m.body::<&str>()? == well_known {
*signaled.lock().unwrap() = true;
}
Ok(())
})
.unwrap();
}
fdo::DBusProxy::new(&conn)
.unwrap()
.request_name(&well_known, fdo::RequestNameFlags::ReplaceExisting.into())
.unwrap();
loop {
proxy.next_signal().unwrap();
if *owner_change_signaled.lock().unwrap() && *name_acquired_signaled.lock().unwrap() {
break;
}
}
}
}