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