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