use futures_util::StreamExt;
use static_assertions::assert_impl_all;
use std::{
convert::{TryFrom, TryInto},
ops::Deref,
sync::Arc,
};
use zbus_names::{BusName, InterfaceName, MemberName, UniqueName};
use zvariant::{ObjectPath, OwnedValue, Value};
use crate::{blocking::Connection, utils::block_on, Error, Message, Result};
use crate::fdo;
#[derive(derivative::Derivative)]
#[derivative(Clone, Debug)]
pub struct Proxy<'a> {
#[derivative(Debug = "ignore")]
conn: Connection,
azync: Option<crate::Proxy<'a>>,
}
assert_impl_all!(Proxy<'_>: Send, Sync, Unpin);
impl<'a> Proxy<'a> {
pub fn new<D, P, I>(
conn: &Connection,
destination: D,
path: P,
interface: I,
) -> Result<Proxy<'a>>
where
D: TryInto<BusName<'a>>,
P: TryInto<ObjectPath<'a>>,
I: TryInto<InterfaceName<'a>>,
D::Error: Into<Error>,
P::Error: Into<Error>,
I::Error: Into<Error>,
{
let proxy = block_on(crate::Proxy::new(
conn.inner(),
destination,
path,
interface,
))?;
Ok(Self {
conn: conn.clone(),
azync: Some(proxy),
})
}
pub fn new_owned<D, P, I>(
conn: Connection,
destination: D,
path: P,
interface: I,
) -> Result<Proxy<'a>>
where
D: TryInto<BusName<'static>>,
P: TryInto<ObjectPath<'static>>,
I: TryInto<InterfaceName<'static>>,
D::Error: Into<Error>,
P::Error: Into<Error>,
I::Error: Into<Error>,
{
let proxy = block_on(crate::Proxy::new_owned(
conn.clone().into_inner(),
destination,
path,
interface,
))?;
Ok(Self {
conn,
azync: Some(proxy),
})
}
pub fn connection(&self) -> &Connection {
&self.conn
}
pub fn destination(&self) -> &BusName<'_> {
self.inner().destination()
}
pub fn path(&self) -> &ObjectPath<'_> {
self.inner().path()
}
pub fn interface(&self) -> &InterfaceName<'_> {
self.inner().interface()
}
pub fn introspect(&self) -> fdo::Result<String> {
block_on(self.inner().introspect())
}
pub fn cached_property<T>(&self, property_name: &str) -> Result<Option<T>>
where
T: TryFrom<OwnedValue>,
T::Error: Into<Error>,
{
self.inner().cached_property(property_name)
}
pub fn cached_property_raw<'p>(
&'p self,
property_name: &'p str,
) -> Option<impl Deref<Target = Value<'static>> + 'p> {
self.inner().cached_property_raw(property_name)
}
pub fn get_property<T>(&self, property_name: &str) -> Result<T>
where
T: TryFrom<OwnedValue>,
T::Error: Into<Error>,
{
block_on(self.inner().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.inner().set_property(property_name, value))
}
pub fn call_method<'m, M, B>(&self, method_name: M, body: &B) -> Result<Arc<Message>>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + zvariant::DynamicType,
{
block_on(self.inner().call_method(method_name, body))
}
pub fn call<'m, M, B, R>(&self, method_name: M, body: &B) -> Result<R>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + zvariant::DynamicType,
R: serde::de::DeserializeOwned + zvariant::Type,
{
block_on(self.inner().call(method_name, body))
}
pub fn call_noreply<'m, M, B>(&self, method_name: M, body: &B) -> Result<()>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + zvariant::DynamicType,
{
block_on(self.inner().call_noreply(method_name, body))
}
pub fn receive_signal<'m: 'a, M>(&self, signal_name: M) -> Result<SignalIterator<'a>>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
{
block_on(self.inner().receive_signal(signal_name))
.map(Some)
.map(SignalIterator)
}
pub fn receive_all_signals(&self) -> Result<SignalIterator<'a>> {
block_on(self.inner().receive_all_signals())
.map(Some)
.map(SignalIterator)
}
pub fn receive_property_changed<'name: 'a, T>(
&self,
name: &'name str,
) -> PropertyIterator<'a, T> {
PropertyIterator(block_on(self.inner().receive_property_changed(name)))
}
pub fn receive_owner_changed(&self) -> Result<OwnerChangedIterator<'_>> {
block_on(self.inner().receive_owner_changed()).map(OwnerChangedIterator)
}
pub fn inner(&self) -> &crate::Proxy<'a> {
self.azync.as_ref().expect("Inner proxy is `None`")
}
pub fn into_inner(mut self) -> crate::Proxy<'a> {
self.azync.take().expect("Inner proxy is `None`")
}
}
impl<'a> std::convert::AsRef<Proxy<'a>> for Proxy<'a> {
fn as_ref(&self) -> &Proxy<'a> {
self
}
}
impl<'a> From<crate::Proxy<'a>> for Proxy<'a> {
fn from(proxy: crate::Proxy<'a>) -> Self {
Self {
conn: proxy.connection().clone().into(),
azync: Some(proxy),
}
}
}
impl std::ops::Drop for Proxy<'_> {
fn drop(&mut self) {
block_on(async {
self.azync.take();
});
}
}
#[derive(Debug)]
pub struct SignalIterator<'a>(Option<crate::SignalStream<'a>>);
assert_impl_all!(SignalIterator<'_>: Send, Sync, Unpin);
impl std::iter::Iterator for SignalIterator<'_> {
type Item = Arc<Message>;
fn next(&mut self) -> Option<Self::Item> {
block_on(self.0.as_mut().expect("`SignalStream` is `None`").next())
}
}
impl std::ops::Drop for SignalIterator<'_> {
fn drop(&mut self) {
block_on(async {
self.0.take();
});
}
}
pub struct PropertyIterator<'a, T>(crate::PropertyStream<'a, T>);
impl<'a, T> std::iter::Iterator for PropertyIterator<'a, T>
where
T: Unpin,
{
type Item = PropertyChanged<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
block_on(self.0.next()).map(PropertyChanged)
}
}
pub struct PropertyChanged<'a, T>(crate::PropertyChanged<'a, T>);
impl<'a, T> PropertyChanged<'a, T> {
pub fn name(&self) -> &str {
self.0.name()
}
pub fn get_raw(&self) -> Result<impl Deref<Target = Value<'static>> + '_> {
block_on(self.0.get_raw())
}
}
impl<'a, T> PropertyChanged<'a, T>
where
T: TryFrom<zvariant::OwnedValue>,
T::Error: Into<crate::Error>,
{
pub fn get(&self) -> Result<T> {
block_on(self.0.get())
}
}
pub struct OwnerChangedIterator<'a>(crate::OwnerChangedStream<'a>);
impl OwnerChangedIterator<'_> {
pub fn name(&self) -> &BusName<'_> {
self.0.name()
}
}
impl<'a> std::iter::Iterator for OwnerChangedIterator<'a> {
type Item = Option<UniqueName<'static>>;
fn next(&mut self) -> Option<Self::Item> {
block_on(self.0.next())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::blocking;
use ntest::timeout;
use test_log::test;
#[test]
#[timeout(15000)]
fn signal() {
let conn = Connection::session().unwrap();
let proxy = blocking::fdo::DBusProxy::new(&conn).unwrap();
let owner_changed = proxy.receive_name_owner_changed().unwrap();
let name_acquired = proxy.receive_name_acquired().unwrap();
let well_known = "org.freedesktop.zbus.ProxySignalTest";
let unique_name = conn.unique_name().unwrap().to_string();
blocking::fdo::DBusProxy::new(&conn)
.unwrap()
.request_name(
well_known.try_into().unwrap(),
fdo::RequestNameFlags::ReplaceExisting.into(),
)
.unwrap();
for signal in owner_changed {
let args = signal.args().unwrap();
if args.name() == well_known {
assert_eq!(*args.new_owner().as_ref().unwrap(), *unique_name);
break;
}
}
for signal in name_acquired {
if signal.args().unwrap().name() == well_known {
break;
}
}
}
}