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