use enumflags2::BitFlags;
use futures_lite::StreamExt;
use std::{fmt, ops::Deref};
use zbus_names::{BusName, InterfaceName, MemberName, UniqueName};
use zvariant::{ObjectPath, OwnedValue, Value};
use crate::{
blocking::Connection,
message::Message,
proxy::{Defaults, MethodFlags},
utils::block_on,
Error, Result,
};
use crate::fdo;
mod builder;
pub use builder::Builder;
#[derive(Clone)]
pub struct Proxy<'a> {
conn: Connection,
azync: Option<crate::Proxy<'a>>,
}
impl fmt::Debug for Proxy<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Proxy")
.field("azync", &self.azync)
.finish_non_exhaustive()
}
}
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<'a> {
self.inner().destination()
}
pub fn path(&self) -> &ObjectPath<'a> {
self.inner().path()
}
pub fn interface(&self) -> &InterfaceName<'a> {
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>(&self, property_name: &str, value: T) -> fdo::Result<()>
where
T: '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<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: for<'d> zvariant::DynamicDeserialize<'d>,
{
block_on(self.inner().call(method_name, body))
}
pub fn call_with_flags<'m, M, B, R>(
&self,
method_name: M,
flags: BitFlags<MethodFlags>,
body: &B,
) -> Result<Option<R>>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
B: serde::ser::Serialize + zvariant::DynamicType,
R: for<'d> zvariant::DynamicDeserialize<'d>,
{
block_on(self.inner().call_with_flags(method_name, flags, 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, M>(&self, signal_name: M) -> Result<SignalIterator<'m>>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
{
self.receive_signal_with_args(signal_name, &[])
}
pub fn receive_signal_with_args<'m, M>(
&self,
signal_name: M,
args: &[(u8, &str)],
) -> Result<SignalIterator<'m>>
where
M: TryInto<MemberName<'m>>,
M::Error: Into<Error>,
{
block_on(self.inner().receive_signal_with_args(signal_name, args))
.map(Some)
.map(SignalIterator)
}
pub fn receive_all_signals(&self) -> Result<SignalIterator<'static>> {
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<'a>> {
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 Defaults for Proxy<'_> {
const INTERFACE: &'static Option<InterfaceName<'static>> = &None;
const DESTINATION: &'static Option<BusName<'static>> = &None;
const PATH: &'static Option<ObjectPath<'static>> = &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::proxy::SignalStream<'a>>);
impl<'a> SignalIterator<'a> {
pub fn name(&self) -> Option<&MemberName<'a>> {
self.0.as_ref().expect("`SignalStream` is `None`").name()
}
}
impl std::iter::Iterator for SignalIterator<'_> {
type Item = 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 {
if let Some(azync) = self.0.take() {
crate::AsyncDrop::async_drop(azync).await;
}
});
}
}
pub struct PropertyIterator<'a, T>(crate::proxy::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::proxy::PropertyChanged<'a, T>);
impl<T> PropertyChanged<'_, 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<T> PropertyChanged<'_, 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::proxy::OwnerChangedStream<'a>);
impl<'a> OwnerChangedIterator<'a> {
pub fn name(&self) -> &BusName<'a> {
self.0.name()
}
}
impl std::iter::Iterator for OwnerChangedIterator<'_> {
type Item = Option<UniqueName<'static>>;
fn next(&mut self) -> Option<Self::Item> {
block_on(self.0.next())
}
}
pub trait ProxyImpl<'p>
where
Self: Sized,
{
fn builder(conn: &Connection) -> Builder<'p, Self>;
fn into_inner(self) -> Proxy<'p>;
fn inner(&self) -> &Proxy<'p>;
}
#[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 unique_name = conn.unique_name().unwrap().to_string();
let proxy = blocking::fdo::DBusProxy::new(&conn).unwrap();
let well_known = "org.freedesktop.zbus.ProxySignalTest";
let mut owner_changed = proxy
.receive_name_owner_changed_with_args(&[(0, well_known), (2, unique_name.as_str())])
.unwrap();
let mut name_acquired = proxy
.receive_name_acquired_with_args(&[(0, well_known)])
.unwrap();
blocking::fdo::DBusProxy::new(&conn)
.unwrap()
.request_name(
well_known.try_into().unwrap(),
fdo::RequestNameFlags::ReplaceExisting.into(),
)
.unwrap();
let signal = owner_changed.next().unwrap();
let args = signal.args().unwrap();
assert!(args.name() == well_known);
assert!(*args.new_owner().as_ref().unwrap() == *unique_name);
let signal = name_acquired.next().unwrap();
assert!(signal.args().unwrap().name() == well_known);
}
}