Skip to main content

zbus/blocking/proxy/
mod.rs

1//! The client-side proxy API.
2
3use enumflags2::BitFlags;
4use futures_lite::StreamExt;
5use std::{fmt, ops::Deref};
6use zbus_names::{BusName, InterfaceName, MemberName, UniqueName};
7use zvariant::{ObjectPath, OwnedValue, Value};
8
9use crate::{
10    Error, Result,
11    blocking::Connection,
12    message::Message,
13    proxy::{Defaults, MethodFlags},
14    utils::block_on,
15};
16
17use crate::fdo;
18
19mod builder;
20pub use builder::Builder;
21
22/// A blocking wrapper of [`crate::Proxy`].
23///
24/// This API is mostly the same as [`crate::Proxy`], except that all its methods block to
25/// completion.
26///
27/// # Example
28///
29/// ```
30/// use std::result::Result;
31/// use std::error::Error;
32/// use zbus::blocking::{Connection, Proxy};
33///
34/// fn main() -> Result<(), Box<dyn Error>> {
35///     let connection = Connection::session()?;
36///     let p = Proxy::new(
37///         &connection,
38///         "org.freedesktop.DBus",
39///         "/org/freedesktop/DBus",
40///         "org.freedesktop.DBus",
41///     )?;
42///     // owned return value
43///     let _id: String = p.call("GetId", &())?;
44///     // borrowed return value
45///     let body = p.call_method("GetId", &())?.body();
46///     let _id: &str = body.deserialize()?;
47///     Ok(())
48/// }
49/// ```
50///
51/// # Note
52///
53/// It is recommended to use the [`macro@crate::proxy`] macro, which provides a more
54/// convenient and type-safe *façade* `Proxy` derived from a Rust trait.
55///
56/// ## Current limitations:
57///
58/// At the moment, `Proxy` doesn't prevent [auto-launching][al].
59///
60/// [al]: https://github.com/z-galaxy/zbus/issues/54
61#[derive(Clone)]
62pub struct Proxy<'a> {
63    conn: Connection,
64    // Wrap it in an `Option` to ensure the proxy is dropped in a `block_on` call. This is needed
65    // for tokio because the proxy spawns a task in its `Drop` impl and that needs a runtime
66    // context in case of tokio.
67    azync: Option<crate::Proxy<'a>>,
68}
69
70impl fmt::Debug for Proxy<'_> {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        f.debug_struct("Proxy")
73            .field("azync", &self.azync)
74            .finish_non_exhaustive()
75    }
76}
77
78impl<'a> Proxy<'a> {
79    /// Create a new `Proxy` for the given destination/path/interface.
80    pub fn new<D, P, I>(
81        conn: &Connection,
82        destination: D,
83        path: P,
84        interface: I,
85    ) -> Result<Proxy<'a>>
86    where
87        D: TryInto<BusName<'a>>,
88        P: TryInto<ObjectPath<'a>>,
89        I: TryInto<InterfaceName<'a>>,
90        D::Error: Into<Error>,
91        P::Error: Into<Error>,
92        I::Error: Into<Error>,
93    {
94        let proxy = block_on(crate::Proxy::new(
95            conn.inner(),
96            destination,
97            path,
98            interface,
99        ))?;
100
101        Ok(Self {
102            conn: conn.clone(),
103            azync: Some(proxy),
104        })
105    }
106
107    /// Create a new `Proxy` for the given destination/path/interface, taking ownership of all
108    /// passed arguments.
109    pub fn new_owned<D, P, I>(
110        conn: Connection,
111        destination: D,
112        path: P,
113        interface: I,
114    ) -> Result<Proxy<'a>>
115    where
116        D: TryInto<BusName<'static>>,
117        P: TryInto<ObjectPath<'static>>,
118        I: TryInto<InterfaceName<'static>>,
119        D::Error: Into<Error>,
120        P::Error: Into<Error>,
121        I::Error: Into<Error>,
122    {
123        let proxy = block_on(crate::Proxy::new_owned(
124            conn.clone().into_inner(),
125            destination,
126            path,
127            interface,
128        ))?;
129
130        Ok(Self {
131            conn,
132            azync: Some(proxy),
133        })
134    }
135
136    /// Get a reference to the associated connection.
137    pub fn connection(&self) -> &Connection {
138        &self.conn
139    }
140
141    /// Get a reference to the destination service name.
142    pub fn destination(&self) -> &BusName<'a> {
143        self.inner().destination()
144    }
145
146    /// Get a reference to the object path.
147    pub fn path(&self) -> &ObjectPath<'a> {
148        self.inner().path()
149    }
150
151    /// Get a reference to the interface.
152    pub fn interface(&self) -> &InterfaceName<'a> {
153        self.inner().interface()
154    }
155
156    /// Introspect the associated object, and return the XML description.
157    ///
158    /// See the [xml](https://docs.rs/zbus_xml) crate for parsing the result.
159    pub fn introspect(&self) -> fdo::Result<String> {
160        block_on(self.inner().introspect())
161    }
162
163    /// Get the cached value of the property `property_name`.
164    ///
165    /// This returns `None` if the property is not in the cache.  This could be because the cache
166    /// was invalidated by an update, because caching was disabled for this property or proxy, or
167    /// because the cache has not yet been populated.  Use `get_property` to fetch the value from
168    /// the peer.
169    pub fn cached_property<T>(&self, property_name: &str) -> Result<Option<T>>
170    where
171        T: TryFrom<OwnedValue>,
172        T::Error: Into<Error>,
173    {
174        self.inner().cached_property(property_name)
175    }
176
177    /// Get the cached value of the property `property_name`.
178    ///
179    /// Same as `cached_property`, but gives you access to the raw value stored in the cache. This
180    /// is useful if you want to avoid allocations and cloning.
181    pub fn cached_property_raw<'p>(
182        &'p self,
183        property_name: &'p str,
184    ) -> Option<impl Deref<Target = Value<'static>> + 'p> {
185        self.inner().cached_property_raw(property_name)
186    }
187
188    /// Get the property `property_name`.
189    ///
190    /// Get the property value from the cache or call the `Get` method of the
191    /// `org.freedesktop.DBus.Properties` interface.
192    pub fn get_property<T>(&self, property_name: &str) -> Result<T>
193    where
194        T: TryFrom<OwnedValue>,
195        T::Error: Into<Error>,
196    {
197        block_on(self.inner().get_property(property_name))
198    }
199
200    /// Set the property `property_name`.
201    ///
202    /// Effectively, call the `Set` method of the `org.freedesktop.DBus.Properties` interface.
203    pub fn set_property<'t, T>(&self, property_name: &str, value: T) -> fdo::Result<()>
204    where
205        T: 't + Into<Value<'t>>,
206    {
207        block_on(self.inner().set_property(property_name, value))
208    }
209
210    /// Call a method and return the reply.
211    ///
212    /// Typically, you would want to use [`call`] method instead. Use this method if you need to
213    /// deserialize the reply message manually (this way, you can avoid the memory
214    /// allocation/copying, by deserializing the reply to an unowned type).
215    ///
216    /// [`call`]: struct.Proxy.html#method.call
217    pub fn call_method<'m, M, B>(&self, method_name: M, body: &B) -> Result<Message>
218    where
219        M: TryInto<MemberName<'m>>,
220        M::Error: Into<Error>,
221        B: serde::ser::Serialize + zvariant::DynamicType,
222    {
223        block_on(self.inner().call_method(method_name, body))
224    }
225
226    /// Call a method and return the reply body.
227    ///
228    /// Use [`call_method`] instead if you need to deserialize the reply manually/separately.
229    ///
230    /// [`call_method`]: struct.Proxy.html#method.call_method
231    pub fn call<'m, M, B, R>(&self, method_name: M, body: &B) -> Result<R>
232    where
233        M: TryInto<MemberName<'m>>,
234        M::Error: Into<Error>,
235        B: serde::ser::Serialize + zvariant::DynamicType,
236        R: for<'d> zvariant::DynamicDeserialize<'d>,
237    {
238        block_on(self.inner().call(method_name, body))
239    }
240
241    /// Call a method and return the reply body, optionally supplying a set of
242    /// method flags to control the way the method call message is sent and handled.
243    ///
244    /// Use [`call`] instead if you do not need any special handling via additional flags.
245    /// If the `NoReplyExpected` flag is passed, this will return None immediately
246    /// after sending the message, similar to [`call_noreply`].
247    ///
248    /// [`call`]: struct.Proxy.html#method.call
249    /// [`call_noreply`]: struct.Proxy.html#method.call_noreply
250    pub fn call_with_flags<'m, M, B, R>(
251        &self,
252        method_name: M,
253        flags: BitFlags<MethodFlags>,
254        body: &B,
255    ) -> Result<Option<R>>
256    where
257        M: TryInto<MemberName<'m>>,
258        M::Error: Into<Error>,
259        B: serde::ser::Serialize + zvariant::DynamicType,
260        R: for<'d> zvariant::DynamicDeserialize<'d>,
261    {
262        block_on(self.inner().call_with_flags(method_name, flags, body))
263    }
264
265    /// Call a method without expecting a reply.
266    ///
267    /// This sets the `NoReplyExpected` flag on the calling message and does not wait for a reply.
268    pub fn call_noreply<'m, M, B>(&self, method_name: M, body: &B) -> Result<()>
269    where
270        M: TryInto<MemberName<'m>>,
271        M::Error: Into<Error>,
272        B: serde::ser::Serialize + zvariant::DynamicType,
273    {
274        block_on(self.inner().call_noreply(method_name, body))
275    }
276
277    /// Create a stream for signal named `signal_name`.
278    ///
279    /// # Errors
280    ///
281    /// Apart from general I/O errors that can result from socket communications, calling this
282    /// method will also result in an error if the destination service has not yet registered its
283    /// well-known name with the bus (assuming you're using the well-known name as destination).
284    pub fn receive_signal<'m, M>(&self, signal_name: M) -> Result<SignalIterator<'m>>
285    where
286        M: TryInto<MemberName<'m>>,
287        M::Error: Into<Error>,
288    {
289        self.receive_signal_with_args(signal_name, &[])
290    }
291
292    /// Same as [`Proxy::receive_signal`] but with a filter.
293    ///
294    /// The D-Bus specification allows you to filter signals by their arguments, which helps avoid
295    /// a lot of unnecessary traffic and processing since the filter is run on the server side. Use
296    /// this method where possible. Note that this filtering is limited to arguments of string
297    /// types.
298    ///
299    /// The arguments are passed as tuples of argument index and expected value.
300    pub fn receive_signal_with_args<'m, M>(
301        &self,
302        signal_name: M,
303        args: &[(u8, &str)],
304    ) -> Result<SignalIterator<'m>>
305    where
306        M: TryInto<MemberName<'m>>,
307        M::Error: Into<Error>,
308    {
309        block_on(self.inner().receive_signal_with_args(signal_name, args))
310            .map(Some)
311            .map(SignalIterator)
312    }
313
314    /// Create a stream for all signals emitted by this service.
315    ///
316    /// # Errors
317    ///
318    /// Apart from general I/O errors that can result from socket communications, calling this
319    /// method will also result in an error if the destination service has not yet registered its
320    /// well-known name with the bus (assuming you're using the well-known name as destination).
321    pub fn receive_all_signals(&self) -> Result<SignalIterator<'static>> {
322        block_on(self.inner().receive_all_signals())
323            .map(Some)
324            .map(SignalIterator)
325    }
326
327    /// Get an iterator to receive property changed events.
328    ///
329    /// Note that zbus doesn't queue the updates. If the listener is slower than the receiver, it
330    /// will only receive the last update.
331    pub fn receive_property_changed<'name: 'a, T>(
332        &self,
333        name: &'name str,
334    ) -> PropertyIterator<'a, T> {
335        PropertyIterator(block_on(self.inner().receive_property_changed(name)))
336    }
337
338    /// Get an iterator to receive owner changed events.
339    ///
340    /// If the proxy destination is a unique name, the stream will be notified of the peer
341    /// disconnection from the bus (with a `None` value).
342    ///
343    /// If the proxy destination is a well-known name, the stream will be notified whenever the name
344    /// owner is changed, either by a new peer being granted ownership (`Some` value) or when the
345    /// name is released (with a `None` value).
346    ///
347    /// Note that zbus doesn't queue the updates. If the listener is slower than the receiver, it
348    /// will only receive the last update.
349    pub fn receive_owner_changed(&self) -> Result<OwnerChangedIterator<'a>> {
350        block_on(self.inner().receive_owner_changed()).map(OwnerChangedIterator)
351    }
352
353    /// Get a reference to the underlying async Proxy.
354    pub fn inner(&self) -> &crate::Proxy<'a> {
355        self.azync.as_ref().expect("Inner proxy is `None`")
356    }
357
358    /// Get the underlying async Proxy, consuming `self`.
359    pub fn into_inner(mut self) -> crate::Proxy<'a> {
360        self.azync.take().expect("Inner proxy is `None`")
361    }
362}
363
364impl Defaults for Proxy<'_> {
365    const INTERFACE: &'static Option<InterfaceName<'static>> = &None;
366    const DESTINATION: &'static Option<BusName<'static>> = &None;
367    const PATH: &'static Option<ObjectPath<'static>> = &None;
368}
369
370impl<'a> std::convert::AsRef<Proxy<'a>> for Proxy<'a> {
371    fn as_ref(&self) -> &Proxy<'a> {
372        self
373    }
374}
375
376impl<'a> From<crate::Proxy<'a>> for Proxy<'a> {
377    fn from(proxy: crate::Proxy<'a>) -> Self {
378        Self {
379            conn: proxy.connection().clone().into(),
380            azync: Some(proxy),
381        }
382    }
383}
384
385impl std::ops::Drop for Proxy<'_> {
386    fn drop(&mut self) {
387        block_on(async {
388            self.azync.take();
389        });
390    }
391}
392
393/// An [`std::iter::Iterator`] implementation that yields signal [messages](`Message`).
394///
395/// Use [`Proxy::receive_signal`] to create an instance of this type.
396#[derive(Debug)]
397pub struct SignalIterator<'a>(Option<crate::proxy::SignalStream<'a>>);
398
399impl<'a> SignalIterator<'a> {
400    /// The signal name.
401    pub fn name(&self) -> Option<&MemberName<'a>> {
402        self.0.as_ref().expect("`SignalStream` is `None`").name()
403    }
404}
405
406impl std::iter::Iterator for SignalIterator<'_> {
407    type Item = Message;
408
409    fn next(&mut self) -> Option<Self::Item> {
410        block_on(self.0.as_mut().expect("`SignalStream` is `None`").next())
411    }
412}
413
414impl std::ops::Drop for SignalIterator<'_> {
415    fn drop(&mut self) {
416        block_on(async {
417            if let Some(azync) = self.0.take() {
418                crate::AsyncDrop::async_drop(azync).await;
419            }
420        });
421    }
422}
423
424/// An [`std::iter::Iterator`] implementation that yields property change notifications.
425///
426/// Use [`Proxy::receive_property_changed`] to create an instance of this type.
427pub struct PropertyIterator<'a, T>(crate::proxy::PropertyStream<'a, T>);
428
429impl<'a, T> std::iter::Iterator for PropertyIterator<'a, T>
430where
431    T: Unpin,
432{
433    type Item = PropertyChanged<'a, T>;
434
435    fn next(&mut self) -> Option<Self::Item> {
436        block_on(self.0.next()).map(PropertyChanged)
437    }
438}
439
440/// A property changed event.
441///
442/// The property changed event generated by [`PropertyIterator`].
443pub struct PropertyChanged<'a, T>(crate::proxy::PropertyChanged<'a, T>);
444
445// split this out to avoid the trait bound on `name` method
446impl<T> PropertyChanged<'_, T> {
447    /// Get the name of the property that changed.
448    pub fn name(&self) -> &str {
449        self.0.name()
450    }
451
452    /// Get the raw value of the property that changed.
453    ///
454    /// If the notification signal contained the new value, it has been cached already and this call
455    /// will return that value. Otherwise (i.e. invalidated property), a D-Bus call is made to fetch
456    /// and cache the new value.
457    pub fn get_raw(&self) -> Result<impl Deref<Target = Value<'static>> + '_> {
458        block_on(self.0.get_raw())
459    }
460}
461
462impl<T> PropertyChanged<'_, T>
463where
464    T: TryFrom<zvariant::OwnedValue>,
465    T::Error: Into<crate::Error>,
466{
467    /// Get the value of the property that changed.
468    ///
469    /// If the notification signal contained the new value, it has been cached already and this call
470    /// will return that value. Otherwise (i.e. invalidated property), a D-Bus call is made to fetch
471    /// and cache the new value.
472    pub fn get(&self) -> Result<T> {
473        block_on(self.0.get())
474    }
475}
476
477/// An [`std::iter::Iterator`] implementation that yields owner change notifications.
478///
479/// Use [`Proxy::receive_owner_changed`] to create an instance of this type.
480pub struct OwnerChangedIterator<'a>(crate::proxy::OwnerChangedStream<'a>);
481
482impl<'a> OwnerChangedIterator<'a> {
483    /// The bus name being tracked.
484    pub fn name(&self) -> &BusName<'a> {
485        self.0.name()
486    }
487}
488
489impl std::iter::Iterator for OwnerChangedIterator<'_> {
490    type Item = Option<UniqueName<'static>>;
491
492    fn next(&mut self) -> Option<Self::Item> {
493        block_on(self.0.next())
494    }
495}
496
497/// This trait is implemented by all blocking proxies, which are generated with the
498/// [`proxy`](macro@zbus::proxy) macro.
499pub trait ProxyImpl<'p>
500where
501    Self: Sized,
502{
503    /// Return a customizable builder for this proxy.
504    fn builder(conn: &Connection) -> Builder<'p, Self>;
505
506    /// Consume `self`, returning the underlying `zbus::Proxy`.
507    fn into_inner(self) -> Proxy<'p>;
508
509    /// The reference to the underlying `zbus::Proxy`.
510    fn inner(&self) -> &Proxy<'p>;
511}
512
513#[cfg(test)]
514mod tests {
515    use super::*;
516    use crate::blocking;
517    use ntest::timeout;
518    use test_log::test;
519
520    #[test]
521    #[timeout(15000)]
522    fn signal() {
523        // Register a well-known name with the session bus and ensure we get the appropriate
524        // signals called for that.
525        let conn = Connection::session().unwrap();
526        let unique_name = conn.unique_name().unwrap().to_string();
527
528        let proxy = blocking::fdo::DBusProxy::new(&conn).unwrap();
529        let well_known = "org.freedesktop.zbus.ProxySignalTest";
530        let mut owner_changed = proxy
531            .receive_name_owner_changed_with_args(&[(0, well_known), (2, unique_name.as_str())])
532            .unwrap();
533        let mut name_acquired = proxy
534            .receive_name_acquired_with_args(&[(0, well_known)])
535            .unwrap();
536
537        blocking::fdo::DBusProxy::new(&conn)
538            .unwrap()
539            .request_name(
540                well_known.try_into().unwrap(),
541                fdo::RequestNameFlags::ReplaceExisting.into(),
542            )
543            .unwrap();
544
545        let signal = owner_changed.next().unwrap();
546        let args = signal.args().unwrap();
547        assert!(args.name() == well_known);
548        assert!(*args.new_owner().as_ref().unwrap() == *unique_name);
549
550        let signal = name_acquired.next().unwrap();
551        // `NameAcquired` is emitted twice, first when the unique name is assigned on
552        // connection and secondly after we ask for a specific name. Let's make sure we only get the
553        // one we subscribed to.
554        assert!(signal.args().unwrap().name() == well_known);
555    }
556}