zbus_macros/lib.rs
1#![deny(rust_2018_idioms)]
2#![doc(
3 html_logo_url = "https://raw.githubusercontent.com/dbus2/zbus/9f7a90d2b594ddc48b7a5f39fda5e00cd56a7dfb/logo.png"
4)]
5#![doc = include_str!("../README.md")]
6#![doc(test(attr(
7 warn(unused),
8 deny(warnings),
9 allow(dead_code),
10 // W/o this, we seem to get some bogus warning about `extern crate zbus`.
11 allow(unused_extern_crates),
12)))]
13
14use proc_macro::TokenStream;
15use syn::{
16 parse_macro_input, punctuated::Punctuated, DeriveInput, ItemImpl, ItemTrait, Meta, Token,
17};
18
19mod error;
20mod iface;
21mod proxy;
22mod utils;
23
24/// Attribute macro for defining D-Bus proxies (using [`zbus::Proxy`] and
25/// [`zbus::blocking::Proxy`]).
26///
27/// The macro must be applied on a `trait T`. Two matching `impl T` will provide an asynchronous
28/// Proxy implementation, named `TraitNameProxy` and a blocking one, named `TraitNameProxyBlocking`.
29/// The proxy instances can be created with the associated `new()` or `builder()` methods. The
30/// former doesn't take any argument and uses the default service name and path. The later allows
31/// you to specify non-default proxy arguments.
32///
33/// The following attributes are supported:
34///
35/// * `interface` - the name of the D-Bus interface this proxy is for.
36///
37/// * `default_service` - the default service this proxy should connect to.
38///
39/// * `default_path` - The default object path the method calls will be sent on and signals will be
40/// sent for by the target service.
41///
42/// * `gen_async` - Whether or not to generate the asynchronous Proxy type.
43///
44/// * `gen_blocking` - Whether or not to generate the blocking Proxy type. If set to `false`, the
45/// asynchronous proxy type will take the name `TraitNameProxy` (i-e no `Async` prefix).
46///
47/// * `async_name` - Specify the exact name of the asynchronous proxy type.
48///
49/// * `blocking_name` - Specify the exact name of the blocking proxy type.
50///
51/// * `assume_defaults` - whether to auto-generate values for `default_path` and `default_service`
52/// if none are specified (default: `false`). `proxy` generates a warning if neither this
53/// attribute nor one of the default values are specified. Please make sure to explicitly set
54/// either this attribute or the default values, according to your needs.
55///
56/// Each trait method will be expanded to call to the associated D-Bus remote interface.
57///
58/// Trait methods accept `proxy` attributes:
59///
60/// * `name` - override the D-Bus name (pascal case form by default)
61///
62/// * `property` - expose the method as a property. If the method takes an argument, it must be a
63/// setter, with a `set_` prefix. Otherwise, it's a getter. Additional sub-attributes exists to
64/// control specific property behaviors:
65/// * `emits_changed_signal` - specifies how property changes are signaled. Valid values are those
66/// documented in [DBus specifications][dbus_emits_changed_signal]:
67/// * `"true"` - (default) change signal is always emitted with the value included. This uses
68/// the default caching behavior of the proxy, and generates a listener method for the change
69/// signal.
70/// * `"invalidates"` - change signal is emitted, but the value is not included in the signal.
71/// This has the same behavior as `"true"`.
72/// * `"const"` - property never changes, thus no signal is ever emitted for it. This uses the
73/// default caching behavior of the proxy, but does not generate a listener method for the
74/// change signal.
75/// * `"false"` - change signal is not (guaranteed to be) emitted if the property changes. This
76/// disables property value caching, and does not generate a listener method for the change
77/// signal.
78///
79/// * `signal` - declare a signal just like a D-Bus method. Read the [Signals](#signals) section
80/// below for details.
81///
82/// * `no_reply` - declare a method call that does not wait for a reply.
83///
84/// * `no_autostart` - declare a method call that will not trigger the bus to automatically launch
85/// the destination service if it is not already running.
86///
87/// * `allow_interactive_auth` - declare a method call that is allowed to trigger an interactive
88/// prompt for authorization or confirmation from the receiver.
89///
90/// * `object` - methods that returns an [`ObjectPath`] can be annotated with the `object` attribute
91/// to specify the proxy object to be constructed from the returned [`ObjectPath`].
92///
93/// * `async_object` - if the assumptions made by `object` attribute about naming of the
94/// asynchronous proxy type, don't fit your bill, you can use this to specify its exact name.
95///
96/// * `blocking_object` - if the assumptions made by `object` attribute about naming of the blocking
97/// proxy type, don't fit your bill, you can use this to specify its exact name.
98///
99/// NB: Any doc comments provided shall be appended to the ones added by the macro.
100///
101/// # Signals
102///
103/// For each signal method declared, this macro will provide a method, named `receive_<method_name>`
104/// to create a [`zbus::SignalStream`] ([`zbus::blocking::SignalIterator`] for the blocking proxy)
105/// wrapper, named `<SignalName>Stream` (`<SignalName>Iterator` for the blocking proxy) that yield
106/// a [`zbus::message::Message`] wrapper, named `<SignalName>`. This wrapper provides type safe
107/// access to the signal arguments. It also implements `Deref<Target = Message>` to allow easy
108/// access to the underlying [`zbus::message::Message`].
109///
110/// # Example
111///
112/// ```no_run
113/// # use std::error::Error;
114/// use zbus_macros::proxy;
115/// use zbus::{blocking::Connection, Result, fdo, zvariant::Value};
116/// use futures_util::stream::StreamExt;
117/// use async_io::block_on;
118///
119/// #[proxy(
120/// interface = "org.test.SomeIface",
121/// default_service = "org.test.SomeService",
122/// default_path = "/org/test/SomeObject"
123/// )]
124/// trait SomeIface {
125/// fn do_this(&self, with: &str, some: u32, arg: &Value<'_>) -> Result<bool>;
126///
127/// #[zbus(property)]
128/// fn a_property(&self) -> fdo::Result<String>;
129///
130/// #[zbus(property)]
131/// fn set_a_property(&self, a_property: &str) -> fdo::Result<()>;
132///
133/// #[zbus(signal)]
134/// fn some_signal(&self, arg1: &str, arg2: u32) -> fdo::Result<()>;
135///
136/// #[zbus(object = "SomeOtherIface", blocking_object = "SomeOtherInterfaceBlock")]
137/// // The method will return a `SomeOtherIfaceProxy` or `SomeOtherIfaceProxyBlock`, depending
138/// // on whether it is called on `SomeIfaceProxy` or `SomeIfaceProxyBlocking`, respectively.
139/// //
140/// // NB: We explicitly specified the exact name of the blocking proxy type. If we hadn't,
141/// // `SomeOtherIfaceProxyBlock` would have been assumed and expected. We could also specify
142/// // the specific name of the asynchronous proxy types, using the `async_object` attribute.
143/// fn some_method(&self, arg1: &str);
144/// }
145///
146/// #[proxy(
147/// interface = "org.test.SomeOtherIface",
148/// default_service = "org.test.SomeOtherService",
149/// blocking_name = "SomeOtherInterfaceBlock",
150/// )]
151/// trait SomeOtherIface {}
152///
153/// let connection = Connection::session()?;
154/// // Use `builder` to override the default arguments, `new` otherwise.
155/// let proxy = SomeIfaceProxyBlocking::builder(&connection)
156/// .destination("org.another.Service")?
157/// .cache_properties(zbus::CacheProperties::No)
158/// .build()?;
159/// let _ = proxy.do_this("foo", 32, &Value::new(true));
160/// let _ = proxy.set_a_property("val");
161///
162/// let signal = proxy.receive_some_signal()?.next().unwrap();
163/// let args = signal.args()?;
164/// println!("arg1: {}, arg2: {}", args.arg1(), args.arg2());
165///
166/// // Now the same again, but asynchronous.
167/// block_on(async move {
168/// let proxy = SomeIfaceProxy::builder(&connection.into())
169/// .cache_properties(zbus::CacheProperties::No)
170/// .build()
171/// .await
172/// .unwrap();
173/// let _ = proxy.do_this("foo", 32, &Value::new(true)).await;
174/// let _ = proxy.set_a_property("val").await;
175///
176/// let signal = proxy.receive_some_signal().await?.next().await.unwrap();
177/// let args = signal.args()?;
178/// println!("arg1: {}, arg2: {}", args.arg1(), args.arg2());
179///
180/// Ok::<(), zbus::Error>(())
181/// })?;
182///
183/// # Ok::<_, Box<dyn Error + Send + Sync>>(())
184/// ```
185///
186/// [`zbus_polkit`] is a good example of how to bind a real D-Bus API.
187///
188/// [`zbus_polkit`]: https://docs.rs/zbus_polkit/1.0.0/zbus_polkit/policykit1/index.html
189/// [`zbus::Proxy`]: https://docs.rs/zbus/latest/zbus/proxy/struct.Proxy.html
190/// [`zbus::message::Message`]: https://docs.rs/zbus/latest/zbus/message/struct.Message.html
191/// [`zbus::blocking::Proxy`]: https://docs.rs/zbus/latest/zbus/blocking/proxy/struct.Proxy.html
192/// [`zbus::SignalStream`]: https://docs.rs/zbus/latest/zbus/proxy/struct.SignalStream.html
193/// [`zbus::blocking::SignalIterator`]: https://docs.rs/zbus/latest/zbus/blocking/proxy/struct.SignalIterator.html
194/// [`ObjectPath`]: https://docs.rs/zvariant/latest/zvariant/struct.ObjectPath.html
195/// [dbus_emits_changed_signal]: https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
196#[proc_macro_attribute]
197pub fn proxy(attr: TokenStream, item: TokenStream) -> TokenStream {
198 let args = parse_macro_input!(attr with Punctuated<Meta, Token![,]>::parse_terminated);
199 let input = parse_macro_input!(item as ItemTrait);
200 proxy::expand::<proxy::TraitAttributes, proxy::MethodAttributes>(args, input)
201 .unwrap_or_else(|err| err.to_compile_error())
202 .into()
203}
204
205#[deprecated = "Use `#[proxy(...)]` proc macro with `#[zbus(...)]` item attributes instead."]
206#[proc_macro_attribute]
207pub fn dbus_proxy(attr: TokenStream, item: TokenStream) -> TokenStream {
208 let args = parse_macro_input!(attr with Punctuated<Meta, Token![,]>::parse_terminated);
209 let input = parse_macro_input!(item as ItemTrait);
210 proxy::expand::<proxy::old::TraitAttributes, proxy::old::MethodAttributes>(args, input)
211 .unwrap_or_else(|err| err.to_compile_error())
212 .into()
213}
214
215/// Attribute macro for implementing a D-Bus interface.
216///
217/// The macro must be applied on an `impl T`. All methods will be exported, either as methods,
218/// properties or signal depending on the item attributes. It will implement the [`Interface`] trait
219/// `for T` on your behalf, to handle the message dispatching and introspection support.
220///
221/// The trait accepts the `interface` attributes:
222///
223/// * `name` - the D-Bus interface name
224///
225/// * `spawn` - Controls the spawning of tasks for method calls. By default, `true`, allowing zbus
226/// to spawn a separate task for each method call. This default behavior can lead to methods being
227/// handled out of their received order, which might not always align with expected or desired
228/// behavior.
229///
230/// - **When True (Default):** Suitable for interfaces where method calls are independent of each
231/// other or can be processed asynchronously without strict ordering. In scenarios where a client
232/// must wait for a reply before making further dependent calls, this default behavior is
233/// appropriate.
234///
235/// - **When False:** Use this setting to ensure methods are handled in the order they are
236/// received, which is crucial for interfaces requiring sequential processing of method calls.
237/// However, care must be taken to avoid making D-Bus method calls from within your interface
238/// methods when this setting is false, as it may lead to deadlocks under certain conditions.
239///
240/// * `proxy` - If specified, a proxy type will also be generated for the interface. This attribute
241/// supports all the [`macro@proxy`]-specific sub-attributes (e.g `gen_async`). The common
242/// sub-attributes (e.g `name`) are automatically forworded to the [`macro@proxy`] macro.
243///
244/// The methods accepts the `interface` attributes:
245///
246/// * `name` - override the D-Bus name (pascal case form of the method by default)
247///
248/// * `property` - expose the method as a property. If the method takes an argument, it must be a
249/// setter, with a `set_` prefix. Otherwise, it's a getter. If it may fail, a property method must
250/// return `zbus::fdo::Result`. An additional sub-attribute exists to control the emission of
251/// signals on changes to the property:
252/// * `emits_changed_signal` - specifies how property changes are signaled. Valid values are those
253/// documented in [DBus specifications][dbus_emits_changed_signal]:
254/// * `"true"` - (default) the change signal is always emitted when the property's setter is
255/// called. The value of the property is included in the signal.
256/// * `"invalidates"` - the change signal is emitted, but the value is not included in the
257/// signal.
258/// * `"const"` - the property never changes, thus no signal is ever emitted for it.
259/// * `"false"` - the change signal is not emitted if the property changes.
260
261/// * `signal` - the method is a "signal". It must be a method declaration (without body). Its code
262/// block will be expanded to emit the signal from the object path associated with the interface
263/// instance.
264///
265/// You can call a signal method from a an interface method, or from an [`ObjectServer::with`]
266/// function.
267///
268/// * `out_args` - When returning multiple values from a method, naming the out arguments become
269/// important. You can use `out_args` to specify their names.
270///
271/// * `proxy` - Use this to specify the [`macro@proxy`]-specific method sub-attributes (e.g
272/// `object`). The common sub-attributes (e.g `name`) are automatically forworded to the
273/// [`macro@proxy`] macro.
274///
275/// In such case, your method must return a tuple containing
276/// your out arguments, in the same order as passed to `out_args`.
277///
278/// The `struct_return` attribute (from zbus 1.x) is no longer supported. If you want to return a
279/// single structure from a method, declare it to return a tuple containing either a named structure
280/// or a nested tuple.
281///
282/// Note: a `<property_name_in_snake_case>_changed` method is generated for each property: this
283/// method emits the "PropertiesChanged" signal for the associated property. The setter (if it
284/// exists) will automatically call this method. For instance, a property setter named `set_foo`
285/// will be called to set the property "Foo", and will emit the "PropertiesChanged" signal with the
286/// new value for "Foo". Other changes to the "Foo" property can be signaled manually with the
287/// generated `foo_changed` method. In addition, a `<property_name_in_snake_case>_invalidated`
288/// method is also generated that much like `_changed` method, emits a "PropertyChanged" signal
289/// but does not send over the new value of the property along with it. It is usually best to avoid
290/// using this since it will force all interested peers to fetch the new value and hence result in
291/// excess traffic on the bus.
292///
293/// The method arguments support the following `zbus` attributes:
294///
295/// * `object_server` - This marks the method argument to receive a reference to the
296/// [`ObjectServer`] this method was called by.
297/// * `connection` - This marks the method argument to receive a reference to the [`Connection`] on
298/// which the method call was received.
299/// * `header` - This marks the method argument to receive the message header associated with the
300/// D-Bus method call being handled.
301/// * `signal_context` - This marks the method argument to receive a [`SignalContext`] instance,
302/// which is needed for emitting signals the easy way.
303///
304/// # Example
305///
306/// ```
307/// # use std::error::Error;
308/// use zbus_macros::interface;
309/// use zbus::{ObjectServer, object_server::SignalContext, message::Header};
310///
311/// struct Example {
312/// _some_data: String,
313/// }
314///
315/// #[interface(name = "org.myservice.Example")]
316/// impl Example {
317/// // "Quit" method. A method may throw errors.
318/// async fn quit(
319/// &self,
320/// #[zbus(header)]
321/// hdr: Header<'_>,
322/// #[zbus(signal_context)]
323/// ctxt: SignalContext<'_>,
324/// #[zbus(object_server)]
325/// _server: &ObjectServer,
326/// ) -> zbus::fdo::Result<()> {
327/// let path = hdr.path().unwrap();
328/// let msg = format!("You are leaving me on the {} path?", path);
329/// Example::bye(&ctxt, &msg).await?;
330///
331/// // Do some asynchronous tasks before quitting..
332///
333/// Ok(())
334/// }
335///
336/// // "TheAnswer" property (note: the "name" attribute), with its associated getter.
337/// // A `the_answer_changed` method has also been generated to emit the
338/// // "PropertiesChanged" signal for this property.
339/// #[zbus(property, name = "TheAnswer")]
340/// fn answer(&self) -> u32 {
341/// 2 * 3 * 7
342/// }
343///
344/// // "IFail" property with its associated getter.
345/// // An `i_fail_changed` method has also been generated to emit the
346/// // "PropertiesChanged" signal for this property.
347/// #[zbus(property)]
348/// fn i_fail(&self) -> zbus::fdo::Result<i32> {
349/// Err(zbus::fdo::Error::UnknownProperty("IFail".into()))
350/// }
351///
352/// // "Bye" signal (note: no implementation body).
353/// #[zbus(signal)]
354/// async fn bye(signal_ctxt: &SignalContext<'_>, message: &str) -> zbus::Result<()>;
355///
356/// #[zbus(out_args("answer", "question"))]
357/// fn meaning_of_life(&self) -> zbus::fdo::Result<(i32, String)> {
358/// Ok((42, String::from("Meaning of life")))
359/// }
360/// }
361///
362/// # Ok::<_, Box<dyn Error + Send + Sync>>(())
363/// ```
364///
365/// See also [`ObjectServer`] documentation to learn how to export an interface over a `Connection`.
366///
367/// [`ObjectServer`]: https://docs.rs/zbus/latest/zbus/object_server/struct.ObjectServer.html
368/// [`ObjectServer::with`]: https://docs.rs/zbus/latest/zbus/object_server/struct.ObjectServer.html#method.with
369/// [`Connection`]: https://docs.rs/zbus/latest/zbus/connection/struct.Connection.html
370/// [`Connection::emit_signal()`]: https://docs.rs/zbus/latest/zbus/connection/struct.Connection.html#method.emit_signal
371/// [`SignalContext`]: https://docs.rs/zbus/latest/zbus/object_server/struct.SignalContext.html
372/// [`Interface`]: https://docs.rs/zbus/latest/zbus/object_server/trait.Interface.html
373/// [dbus_emits_changed_signal]: https://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
374#[proc_macro_attribute]
375pub fn interface(attr: TokenStream, item: TokenStream) -> TokenStream {
376 let args = parse_macro_input!(attr with Punctuated<Meta, Token![,]>::parse_terminated);
377 let input = parse_macro_input!(item as ItemImpl);
378 iface::expand::<iface::ImplAttributes, iface::MethodAttributes>(args, input)
379 .unwrap_or_else(|err| err.to_compile_error())
380 .into()
381}
382
383#[deprecated = "Use `#[interface(...)]` proc macro with `#[zbus(...)]` item attributes instead."]
384#[proc_macro_attribute]
385pub fn dbus_interface(attr: TokenStream, item: TokenStream) -> TokenStream {
386 let args = parse_macro_input!(attr with Punctuated<Meta, Token![,]>::parse_terminated);
387 let input = parse_macro_input!(item as ItemImpl);
388 iface::expand::<iface::old::ImplAttributes, iface::old::MethodAttributes>(args, input)
389 .unwrap_or_else(|err| err.to_compile_error())
390 .into()
391}
392
393/// Derive macro for implementing [`zbus::DBusError`] trait.
394///
395/// This macro makes it easy to implement the [`zbus::DBusError`] trait for your custom error type
396/// (currently only enums are supported).
397///
398/// If a special variant marked with the `zbus` attribute is present, `From<zbus::Error>` is
399/// also implemented for your type. This variant can only have a single unnamed field of type
400/// [`zbus::Error`]. This implementation makes it possible for you to declare proxy methods to
401/// directly return this type, rather than [`zbus::Error`].
402///
403/// Each variant (except for the special `zbus` one) can optionally have a (named or unnamed)
404/// `String` field (which is used as the human-readable error description).
405///
406/// # Example
407///
408/// ```
409/// use zbus_macros::DBusError;
410///
411/// #[derive(DBusError, Debug)]
412/// #[zbus(prefix = "org.myservice.App")]
413/// enum Error {
414/// #[zbus(error)]
415/// ZBus(zbus::Error),
416/// FileNotFound(String),
417/// OutOfMemory,
418/// }
419/// ```
420///
421/// [`zbus::DBusError`]: https://docs.rs/zbus/latest/zbus/trait.DBusError.html
422/// [`zbus::Error`]: https://docs.rs/zbus/latest/zbus/enum.Error.html
423/// [`zvariant::Type`]: https://docs.rs/zvariant/latest/zvariant/trait.Type.html
424/// [`serde::Serialize`]: https://docs.rs/serde/1.0.132/serde/trait.Serialize.html
425#[proc_macro_derive(DBusError, attributes(zbus))]
426pub fn derive_dbus_error(input: TokenStream) -> TokenStream {
427 let input = parse_macro_input!(input as DeriveInput);
428 error::expand_derive(input)
429 .unwrap_or_else(|err| err.to_compile_error())
430 .into()
431}