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