zbus/blocking/proxy/
mod.rs

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