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}