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 | |
14 | use proc_macro::TokenStream; |
15 | use syn::{ |
16 | parse_macro_input, punctuated::Punctuated, DeriveInput, ItemImpl, ItemTrait, Meta, Token, |
17 | }; |
18 | |
19 | mod error; |
20 | mod iface; |
21 | mod proxy; |
22 | mod 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 ] |
197 | pub fn proxy (attr: TokenStream, item: TokenStream) -> TokenStream { |
198 | let args: Punctuated = parse_macro_input!(attr with Punctuated<Meta, Token![,]>::parse_terminated); |
199 | let input: ItemTrait = parse_macro_input!(item as ItemTrait); |
200 | proxyTokenStream::expand::<proxy::TraitAttributes, proxy::MethodAttributes>(args, input) |
201 | .unwrap_or_else(|err: Error| err.to_compile_error()) |
202 | .into() |
203 | } |
204 | |
205 | #[deprecated = "Use `#[proxy(...)]` proc macro with `#[zbus(...)]` item attributes instead." ] |
206 | #[proc_macro_attribute ] |
207 | pub fn dbus_proxy (attr: TokenStream, item: TokenStream) -> TokenStream { |
208 | let args: Punctuated = parse_macro_input!(attr with Punctuated<Meta, Token![,]>::parse_terminated); |
209 | let input: ItemTrait = parse_macro_input!(item as ItemTrait); |
210 | proxyTokenStream::expand::<proxy::old::TraitAttributes, proxy::old::MethodAttributes>(args, input) |
211 | .unwrap_or_else(|err: Error| 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 ] |
375 | pub fn interface (attr: TokenStream, item: TokenStream) -> TokenStream { |
376 | let args: Punctuated = parse_macro_input!(attr with Punctuated<Meta, Token![,]>::parse_terminated); |
377 | let input: ItemImpl = parse_macro_input!(item as ItemImpl); |
378 | ifaceTokenStream::expand::<iface::ImplAttributes, iface::MethodAttributes>(args, input) |
379 | .unwrap_or_else(|err: Error| err.to_compile_error()) |
380 | .into() |
381 | } |
382 | |
383 | #[deprecated = "Use `#[interface(...)]` proc macro with `#[zbus(...)]` item attributes instead." ] |
384 | #[proc_macro_attribute ] |
385 | pub fn dbus_interface (attr: TokenStream, item: TokenStream) -> TokenStream { |
386 | let args: Punctuated = parse_macro_input!(attr with Punctuated<Meta, Token![,]>::parse_terminated); |
387 | let input: ItemImpl = parse_macro_input!(item as ItemImpl); |
388 | ifaceTokenStream::expand::<iface::old::ImplAttributes, iface::old::MethodAttributes>(args, input) |
389 | .unwrap_or_else(|err: Error| 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))] |
426 | pub fn derive_dbus_error(input: TokenStream) -> TokenStream { |
427 | let input: DeriveInput = parse_macro_input!(input as DeriveInput); |
428 | errorTokenStream::expand_derive(input) |
429 | .unwrap_or_else(|err: Error| err.to_compile_error()) |
430 | .into() |
431 | } |
432 | |