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}