1 //! Macros to make working with dbus-rs easier.
2 //!
3 //! This crate provides several macros to make it easier to project Rust types
4 //! and traits onto D-Bus.
5 extern crate proc_macro;
6 
7 use quote::{format_ident, quote, ToTokens};
8 
9 use std::fs::File;
10 use std::io::Write;
11 use std::path::Path;
12 
13 use syn::parse::Parser;
14 use syn::punctuated::Punctuated;
15 use syn::token::Comma;
16 use syn::{Expr, FnArg, ImplItem, ItemImpl, ItemStruct, Meta, NestedMeta, Pat, ReturnType, Type};
17 
18 use crate::proc_macro::TokenStream;
19 
20 const OUTPUT_DEBUG: bool = false;
21 
debug_output_to_file(gen: &proc_macro2::TokenStream, filename: String)22 fn debug_output_to_file(gen: &proc_macro2::TokenStream, filename: String) {
23     if !OUTPUT_DEBUG {
24         return;
25     }
26 
27     let filepath = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap())
28         .join(filename)
29         .to_str()
30         .unwrap()
31         .to_string();
32 
33     let path = Path::new(&filepath);
34     let mut file = File::create(path).unwrap();
35     file.write_all(gen.to_string().as_bytes()).unwrap();
36 }
37 
38 /// Marks a method to be projected to a D-Bus method and specifies the D-Bus method name.
39 ///
40 /// Example:
41 ///   `#[dbus_method("GetAdapterFoo")]`
42 ///   `#[dbus_method("RegisterFoo", DBusLog::Enable(DBusLogOptions::LogAll, DBusLogVerbosity::Info))`
43 ///
44 /// # Args
45 ///
46 /// `dbus_method_name`: String. The D-Bus method name.
47 /// `dbus_logging`: enum DBusLog, optional. Whether to enable logging and the log verbosity.
48 ///                 Note that log is disabled by default for outgoing dbus messages, but enabled
49 ///                 by default with verbose level for incoming messages.
50 #[proc_macro_attribute]
dbus_method(_attr: TokenStream, item: TokenStream) -> TokenStream51 pub fn dbus_method(_attr: TokenStream, item: TokenStream) -> TokenStream {
52     let ori_item: proc_macro2::TokenStream = item.clone().into();
53     let gen = quote! {
54         #[allow(unused_variables)]
55         #ori_item
56     };
57     gen.into()
58 }
59 
60 /// Generates a function to export a Rust object to D-Bus. The result will provide an IFaceToken
61 /// that must then be registered to an object.
62 ///
63 /// Example:
64 ///   `#[generate_dbus_exporter(export_foo_dbus_intf, "org.example.FooInterface")]`
65 ///   `#[generate_dbus_exporter(export_foo_dbus_intf, "org.example.FooInterface", FooMixin, foo)]`
66 ///
67 /// This generates a method called `export_foo_dbus_intf` that will export a Rust object type into a
68 /// interface token for `org.example.FooInterface`. This interface must then be inserted to an
69 /// object in order to be exported.
70 ///
71 /// If the mixin parameter is provided, you must provide the mixin class when registering with
72 /// crossroads (and that's the one that should be Arc<Mutex<...>>.
73 ///
74 /// In order to use the interface via D-Bus calls, the Rust object needs to declare its methods with
75 /// the attribute #[dbus_method()].
76 ///
77 /// # Args
78 ///
79 /// `exporter`: Function name for outputted interface exporter.
80 /// `interface`: Name of the interface where this object should be exported.
81 /// `mixin_type`: The name of the Mixin struct. Mixins should be used when
82 ///               exporting multiple interfaces and objects under a single object
83 ///               path.
84 /// `mixin_name`: Name of this object in the mixin where it's implemented.
85 #[proc_macro_attribute]
generate_dbus_exporter(attr: TokenStream, item: TokenStream) -> TokenStream86 pub fn generate_dbus_exporter(attr: TokenStream, item: TokenStream) -> TokenStream {
87     let ori_item: proc_macro2::TokenStream = item.clone().into();
88 
89     let args = Punctuated::<Expr, Comma>::parse_separated_nonempty.parse(attr.clone()).unwrap();
90 
91     let fn_ident = if let Expr::Path(p) = &args[0] {
92         p.path.get_ident().unwrap()
93     } else {
94         panic!("function name must be specified");
95     };
96 
97     let dbus_iface_name = if let Expr::Lit(lit) = &args[1] {
98         lit
99     } else {
100         panic!("D-Bus interface name must be specified");
101     };
102 
103     // Must provide both a mixin type and name.
104     let (mixin_type, mixin_name) = if args.len() > 3 {
105         match (&args[2], &args[3]) {
106             (Expr::Path(t), Expr::Path(n)) => (Some(t), Some(n)),
107             (_, _) => (None, None),
108         }
109     } else {
110         (None, None)
111     };
112 
113     let ast: ItemImpl = syn::parse(item.clone()).unwrap();
114     let api_iface_ident = ast.trait_.unwrap().1.to_token_stream();
115 
116     let mut register_methods = quote! {};
117 
118     // If the object isn't expected to be part of a mixin, expect the object
119     // type to be Arc<Mutex<Box<T>>>. Otherwise, we accept any type T and depend
120     // on the field name lookup to throw an error.
121     let obj_type = match mixin_type {
122         None => quote! { std::sync::Arc<std::sync::Mutex<Box<T>>> },
123         Some(t) => quote! { Box<#t> },
124     };
125 
126     for item in ast.items {
127         if let ImplItem::Method(method) = item {
128             if method.attrs.len() != 1 {
129                 continue;
130             }
131 
132             let attr = &method.attrs[0];
133             if !attr.path.get_ident().unwrap().to_string().eq("dbus_method") {
134                 continue;
135             }
136 
137             let meta_list = match attr.parse_meta().unwrap() {
138                 Meta::List(meta_list) => meta_list,
139                 _ => continue,
140             };
141 
142             let dbus_method_name = meta_list.nested[0].clone();
143 
144             // logging is default to verbose if not specified
145             let dbus_logging = if meta_list.nested.len() > 1 {
146                 meta_list.nested[1].clone()
147             } else {
148                 let token =
149                     quote! { DBusLog::Enable(DBusLogOptions::LogAll, DBusLogVerbosity::Verbose) };
150                 syn::parse2::<NestedMeta>(token).unwrap()
151             };
152 
153             let method_name = method.sig.ident;
154 
155             let mut arg_names = quote! {};
156             let mut method_args = quote! {};
157             let mut make_args = quote! {};
158             let mut dbus_input_vars = quote! {};
159             let mut dbus_input_types = quote! {};
160             let mut args_debug = quote! {};
161             let mut args_debug_format = String::new();
162 
163             for input in method.sig.inputs {
164                 if let FnArg::Typed(ref typed) = input {
165                     let arg_type = &typed.ty;
166                     if let Pat::Ident(pat_ident) = &*typed.pat {
167                         let ident = pat_ident.ident.clone();
168                         let mut dbus_input_ident = ident.to_string();
169                         dbus_input_ident.push('_');
170                         let dbus_input_arg = format_ident!("{}", dbus_input_ident);
171                         let ident_string = ident.to_string();
172 
173                         arg_names = quote! {
174                             #arg_names #ident_string,
175                         };
176 
177                         method_args = quote! {
178                             #method_args #ident,
179                         };
180 
181                         dbus_input_vars = quote! {
182                             #dbus_input_vars #dbus_input_arg,
183                         };
184 
185                         dbus_input_types = quote! {
186                             #dbus_input_types
187                             <#arg_type as DBusArg>::DBusType,
188                         };
189 
190                         make_args = quote! {
191                             #make_args
192                             let #ident = <#arg_type as DBusArg>::from_dbus(
193                                 #dbus_input_arg,
194                                 Some(conn_clone.clone()),
195                                 Some(ctx.message().sender().unwrap().into_static()),
196                                 Some(dc_watcher_clone.clone()),
197                             );
198 
199                             if let Result::Err(e) = #ident {
200                                 return Err(dbus_crossroads::MethodErr::invalid_arg(
201                                     e.to_string().as_str()
202                                 ));
203                             }
204 
205                             let #ident = #ident.unwrap();
206                         };
207 
208                         args_debug = quote! {
209                             #args_debug
210                             <#arg_type as DBusArg>::log(&#ident),
211                         };
212 
213                         if !args_debug_format.is_empty() {
214                             args_debug_format.push_str(", ");
215                         }
216                         args_debug_format.push_str("|{}|");
217                     }
218                 }
219             }
220 
221             let dbus_input_args = quote! {
222                 (#dbus_input_vars): (#dbus_input_types)
223             };
224 
225             let mut output_names = quote! {};
226             let mut output_type = quote! {};
227             let mut ret = quote! {Ok(())};
228             if let ReturnType::Type(_, t) = method.sig.output {
229                 output_type = quote! {<#t as DBusArg>::DBusType,};
230                 ret = quote! {Ok((<#t as DBusArg>::to_dbus(ret).unwrap(),))};
231                 output_names = quote! { "out", };
232             }
233 
234             let debug = quote! {
235                 let args_formatted = format!(#args_debug_format, #args_debug);
236                 DBusLog::log(#dbus_logging, "dbus in", #dbus_iface_name, #dbus_method_name, args_formatted.as_str());
237             };
238 
239             let method_call = match mixin_name {
240                 Some(name) => {
241                     quote! {
242                         let ret = obj.#name.lock().unwrap().#method_name(#method_args);
243                     }
244                 }
245                 None => {
246                     quote! {
247                         let ret = obj.lock().unwrap().#method_name(#method_args);
248                     }
249                 }
250             };
251 
252             register_methods = quote! {
253                 #register_methods
254 
255                 let conn_clone = conn.clone();
256                 let dc_watcher_clone = disconnect_watcher.clone();
257                 let handle_method = move |ctx: &mut dbus_crossroads::Context,
258                                           obj: &mut #obj_type,
259                                           #dbus_input_args |
260                       -> Result<(#output_type), dbus_crossroads::MethodErr> {
261                     #make_args
262                     #debug
263                     #method_call
264                     #ret
265                 };
266                 ibuilder.method(
267                     #dbus_method_name,
268                     (#arg_names),
269                     (#output_names),
270                     handle_method,
271                 );
272             };
273         }
274     }
275 
276     // If mixin is not given, we enforce the API trait is implemented when exporting.
277     let type_t = match mixin_type {
278         None => quote! { <T: 'static + #api_iface_ident + Send + ?Sized> },
279         Some(_) => quote! {},
280     };
281 
282     let gen = quote! {
283         #ori_item
284 
285         pub fn #fn_ident #type_t(
286             conn: std::sync::Arc<dbus::nonblock::SyncConnection>,
287             cr: &mut dbus_crossroads::Crossroads,
288             disconnect_watcher: std::sync::Arc<std::sync::Mutex<dbus_projection::DisconnectWatcher>>,
289         ) -> dbus_crossroads::IfaceToken<#obj_type> {
290             cr.register(#dbus_iface_name, |ibuilder| {
291                 #register_methods
292             })
293         }
294     };
295 
296     debug_output_to_file(&gen, format!("out-{}.rs", fn_ident));
297 
298     gen.into()
299 }
300 
301 /// Generates a client implementation of a D-Bus interface.
302 ///
303 /// Example:
304 ///   #[generate_dbus_interface_client]
305 ///
306 /// The impl containing #[dbus_method()] will contain a generated code to call the method via D-Bus.
307 ///
308 /// Example:
309 ///   #[generate_dbus_interface_client(SomeRPC)]
310 ///
311 /// When the RPC wrapper struct name is specified, it also generates the more RPC-friendly struct:
312 /// * All methods are async, allowing clients to await (yield) without blocking. Even methods that
313 ///   are sync at the server side requires clients to "wait" for the return.
314 /// * All method returns are wrapped with `Result`, allowing clients to detect D-Bus level errors in
315 ///   addition to API-level errors.
316 #[proc_macro_attribute]
generate_dbus_interface_client(attr: TokenStream, item: TokenStream) -> TokenStream317 pub fn generate_dbus_interface_client(attr: TokenStream, item: TokenStream) -> TokenStream {
318     let rpc_struct_name = attr.to_string();
319 
320     let ast: ItemImpl = syn::parse(item.clone()).unwrap();
321     let trait_path = ast.trait_.unwrap().1;
322     let struct_path = match *ast.self_ty {
323         Type::Path(path) => path,
324         _ => panic!("Struct path not available"),
325     };
326 
327     // Generated methods
328     let mut methods = quote! {};
329 
330     // Generated RPC-friendly methods (async and Result-wrapped).
331     let mut rpc_methods = quote! {};
332 
333     // Iterate on every methods of a trait impl
334     for item in ast.items {
335         if let ImplItem::Method(method) = item {
336             // If the method is not marked with #[dbus_method], just copy the
337             // original method body.
338             if method.attrs.len() != 1 {
339                 methods = quote! {
340                     #methods
341 
342                     #method
343                 };
344                 continue;
345             }
346 
347             let attr = &method.attrs[0];
348             if !attr.path.get_ident().unwrap().to_string().eq("dbus_method") {
349                 continue;
350             }
351 
352             let sig = &method.sig;
353 
354             // For RPC-friendly method, copy the original signature but add public, async, and wrap
355             // the return with Result.
356             let mut rpc_sig = sig.clone();
357             rpc_sig.asyncness = Some(<syn::Token![async]>::default());
358             rpc_sig.output = match rpc_sig.output {
359                 syn::ReturnType::Default => {
360                     syn::parse(quote! {-> Result<(), dbus::Error>}.into()).unwrap()
361                 }
362                 syn::ReturnType::Type(_arrow, path) => {
363                     syn::parse(quote! {-> Result<#path, dbus::Error>}.into()).unwrap()
364                 }
365             };
366             let rpc_sig = quote! {
367                 pub #rpc_sig
368             };
369 
370             let dbus_method_name = if let Meta::List(meta_list) = attr.parse_meta().unwrap() {
371                 Some(meta_list.nested[0].clone())
372             } else {
373                 None
374             };
375 
376             if dbus_method_name.is_none() {
377                 continue;
378             }
379 
380             let mut input_list = quote! {};
381 
382             let mut object_conversions = quote! {};
383 
384             // Iterate on every parameter of a method to build a tuple, e.g.
385             // `(param1, param2, param3)`
386             for input in &method.sig.inputs {
387                 if let FnArg::Typed(ref typed) = input {
388                     let arg_type = &typed.ty;
389                     if let Pat::Ident(pat_ident) = &*typed.pat {
390                         let ident = pat_ident.ident.clone();
391 
392                         let is_box = if let Type::Path(type_path) = &**arg_type {
393                             type_path.path.segments[0].ident.to_string().eq("Box")
394                         } else {
395                             false
396                         };
397 
398                         if is_box {
399                             // A Box<dyn> parameter means this is an object that should be exported
400                             // on D-Bus.
401                             object_conversions = quote! {
402                                 #object_conversions
403                                     let #ident = {
404                                         let path = dbus::Path::new(#ident.get_object_id()).unwrap();
405                                         #ident.export_for_rpc();
406                                         path
407                                     };
408                             };
409                         } else {
410                             // Convert every parameter to its corresponding type recognized by
411                             // the D-Bus library.
412                             object_conversions = quote! {
413                                 #object_conversions
414                                     let #ident = <#arg_type as DBusArg>::to_dbus(#ident).unwrap();
415                             };
416                         }
417                         input_list = quote! {
418                             #input_list
419                             #ident,
420                         };
421                     }
422                 }
423             }
424 
425             let mut output_as_dbus_arg = quote! {};
426             if let ReturnType::Type(_, t) = &method.sig.output {
427                 output_as_dbus_arg = quote! {<#t as DBusArg>};
428             }
429 
430             let input_tuple = quote! {
431                 (#input_list)
432             };
433 
434             let body = match &method.sig.output {
435                 // Build the method call to `self.client_proxy`. `method` or `method_noreturn`
436                 // depends on whether there is a return from the function.
437                 ReturnType::Default => {
438                     quote! {
439                         self.client_proxy.method_noreturn(#dbus_method_name, #input_tuple)
440                     }
441                 }
442                 _ => {
443                     quote! {
444                         let ret: #output_as_dbus_arg::DBusType = self.client_proxy.method(
445                             #dbus_method_name,
446                             #input_tuple,
447                         );
448                         #output_as_dbus_arg::from_dbus(ret, None, None, None).unwrap()
449                     }
450                 }
451             };
452             let rpc_body = match &method.sig.output {
453                 // Build the async method call to `self.client_proxy`.
454                 ReturnType::Default => {
455                     quote! {
456                         self.client_proxy
457                             .async_method_noreturn(#dbus_method_name, #input_tuple)
458                             .await
459                     }
460                 }
461                 _ => {
462                     quote! {
463                         self.client_proxy
464                             .async_method(#dbus_method_name, #input_tuple)
465                             .await
466                             .map(|(x,)| {
467                                 #output_as_dbus_arg::from_dbus(x, None, None, None).unwrap()
468                             })
469                     }
470                 }
471             };
472 
473             // Assemble the method body. May have object conversions if there is a param that is
474             // a proxy object (`Box<dyn>` type).
475             let body = quote! {
476                 #object_conversions
477 
478                 #body
479             };
480             let rpc_body = quote! {
481                 #object_conversions
482 
483                 #rpc_body
484             };
485 
486             // The method definition is its signature and the body.
487             let generated_method = quote! {
488                 #sig {
489                     #body
490                 }
491             };
492             let generated_rpc_method = quote! {
493                 #rpc_sig {
494                     #rpc_body
495                 }
496             };
497 
498             // Assemble all the method definitions.
499             methods = quote! {
500                 #methods
501 
502                 #generated_method
503             };
504             rpc_methods = quote! {
505                 #rpc_methods
506 
507                 #generated_rpc_method
508             };
509         }
510     }
511 
512     // Generated code for the RPC wrapper struct.
513     let rpc_gen = if rpc_struct_name.is_empty() {
514         quote! {}
515     } else {
516         let rpc_struct = format_ident!("{}", rpc_struct_name);
517         quote! {
518             impl #rpc_struct {
519                 #rpc_methods
520             }
521         }
522     };
523 
524     // The final generated code.
525     let gen = quote! {
526         impl #trait_path for #struct_path {
527             #methods
528         }
529 
530         #rpc_gen
531     };
532 
533     debug_output_to_file(
534         &gen,
535         std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap())
536             .join(format!("out-{}.rs", struct_path.path.get_ident().unwrap()))
537             .to_str()
538             .unwrap()
539             .to_string(),
540     );
541 
542     gen.into()
543 }
544 
copy_without_attributes(item: &TokenStream) -> TokenStream545 fn copy_without_attributes(item: &TokenStream) -> TokenStream {
546     let mut ast: ItemStruct = syn::parse(item.clone()).unwrap();
547     for field in &mut ast.fields {
548         field.attrs.clear();
549     }
550 
551     let gen = quote! {
552         #ast
553     };
554 
555     gen.into()
556 }
557 
558 /// Generates a DBusArg implementation to transform Rust plain structs to a D-Bus data structure.
559 ///
560 /// The D-Bus structure constructed by this macro has the signature `a{sv}`.
561 ///
562 /// # Examples
563 ///
564 /// Assume you have a struct as follows:
565 /// ```
566 ///     struct FooBar {
567 ///         foo: i32,
568 ///         bar: u8,
569 ///     }
570 /// ```
571 ///
572 /// In order to serialize this into D-Bus (and deserialize it), you must re-declare this struct
573 /// as follows. Note that the field names must match but the struct name does not.
574 /// ```ignore
575 ///     #[dbus_propmap(FooBar)]
576 ///     struct AnyNameIsFineHere {
577 ///         foo: i32,
578 ///         bar: u8
579 ///     }
580 /// ```
581 ///
582 /// The resulting serialized D-Bus data will look like the following:
583 ///
584 /// ```text
585 /// array [
586 ///     dict {
587 ///         key: "foo",
588 ///         value: Variant(Int32(0))
589 ///     }
590 ///     dict {
591 ///         key: "bar",
592 ///         value: Variant(Byte(0))
593 ///     }
594 /// ]
595 /// ```
596 // TODO: Support more data types of struct fields (currently only supports integers and enums).
597 #[proc_macro_attribute]
dbus_propmap(attr: TokenStream, item: TokenStream) -> TokenStream598 pub fn dbus_propmap(attr: TokenStream, item: TokenStream) -> TokenStream {
599     let ori_item: proc_macro2::TokenStream = copy_without_attributes(&item).into();
600 
601     let ast: ItemStruct = syn::parse(item.clone()).unwrap();
602 
603     let args = Punctuated::<Expr, Comma>::parse_separated_nonempty.parse(attr.clone()).unwrap();
604     let struct_ident =
605         if let Expr::Path(p) = &args[0] { p.path.get_ident().unwrap().clone() } else { ast.ident };
606 
607     let struct_str = struct_ident.to_string();
608 
609     let mut make_fields = quote! {};
610     let mut field_idents = quote! {};
611 
612     let mut insert_map_fields = quote! {};
613 
614     let mut log_format = String::new();
615     let mut log_args = quote! {};
616 
617     for field in ast.fields {
618         let Some(field_ident) = field.ident else { continue };
619 
620         let field_str = field_ident.to_string();
621 
622         let field_type = if let Type::Path(t) = field.ty {
623             t
624         } else {
625             continue;
626         };
627 
628         field_idents = quote! {
629             #field_idents #field_ident,
630         };
631 
632         let field_type_name = format_ident! {"{}_type_", field_str};
633         let make_field = quote! {
634             match #field_ident.arg_type() {
635                 dbus::arg::ArgType::Variant => {}
636                 _ => {
637                     return Err(Box::new(DBusArgError::new(format!(
638                         "{}.{} must be a variant",
639                         #struct_str, #field_str
640                     ))));
641                 }
642             };
643             let #field_ident = <<#field_type as DBusArg>::DBusType as RefArgToRust>::ref_arg_to_rust(
644                 #field_ident.as_static_inner(0).unwrap(),
645                 format!("{}.{}", #struct_str, #field_str),
646             )?;
647             type #field_type_name = #field_type;
648             let #field_ident = #field_type_name::from_dbus(
649                 #field_ident,
650                 conn__.clone(),
651                 remote__.clone(),
652                 disconnect_watcher__.clone(),
653             )?;
654         };
655 
656         make_fields = quote! {
657             #make_fields
658 
659             let #field_ident = match data__.get(#field_str) {
660                 Some(data) => data,
661                 None => {
662                     return Err(Box::new(DBusArgError::new(format!(
663                         "{}.{} is required",
664                         #struct_str, #field_str
665                     ))));
666                 }
667             };
668             #make_field
669         };
670 
671         insert_map_fields = quote! {
672             #insert_map_fields
673             let field_data__ = DBusArg::to_dbus(data__.#field_ident)?;
674             map__.insert(String::from(#field_str), dbus::arg::Variant(Box::new(field_data__)));
675         };
676 
677         if !log_format.is_empty() {
678             log_format.push_str(", ");
679         }
680         log_format.push_str(field_str.as_str());
681         log_format.push_str(": {}");
682 
683         log_args = quote! {
684             #log_args
685             <#field_type as DBusArg>::log(&data__.#field_ident),
686         };
687     }
688 
689     // Give an example type: struct BluetoothDevice { address: RawAddress, name: String }
690     // At this point the |log_format| would be: "address: {}, name: {}"
691     // Now, wrap it with curly braces and prepend the structure name so it becomes:
692     //     "BluetoothDevice { address: {}, name: {} }"
693     log_format.insert_str(0, " {{ ");
694     log_format.push_str(" }}");
695     log_format.insert_str(0, struct_ident.to_string().as_str());
696 
697     let gen = quote! {
698         #[allow(dead_code)]
699         #ori_item
700 
701         impl DBusArg for #struct_ident {
702             type DBusType = dbus::arg::PropMap;
703 
704             fn from_dbus(
705                 data__: dbus::arg::PropMap,
706                 conn__: Option<std::sync::Arc<dbus::nonblock::SyncConnection>>,
707                 remote__: Option<dbus::strings::BusName<'static>>,
708                 disconnect_watcher__: Option<std::sync::Arc<std::sync::Mutex<dbus_projection::DisconnectWatcher>>>,
709             ) -> Result<#struct_ident, Box<dyn std::error::Error>> {
710                 #make_fields
711 
712                 return Ok(#struct_ident {
713                     #field_idents
714                 });
715             }
716 
717             fn to_dbus(data__: #struct_ident) -> Result<dbus::arg::PropMap, Box<dyn std::error::Error>> {
718                 let mut map__: dbus::arg::PropMap = std::collections::HashMap::new();
719                 #insert_map_fields
720                 return Ok(map__);
721             }
722 
723             fn log(data__: &#struct_ident) -> String {
724                 format!(#log_format, #log_args)
725             }
726         }
727     };
728 
729     debug_output_to_file(&gen, format!("out-{}.rs", struct_ident));
730 
731     gen.into()
732 }
733 
734 /// Generates a DBusArg implementation of a Remote RPC proxy object.
735 ///
736 /// Example:
737 ///   `#[dbus_proxy_obj(FooCallback, "org.example.FooCallbackInterface")]`
738 ///
739 /// In order to call the remote methods, declare them with the attribute #[dbus_method()].
740 ///
741 /// # Args
742 ///
743 /// `struct_ident`: A freeform name used to identify the object struct.
744 /// `dbus_iface_name`: Name of the interface where this object should be exported.
745 #[proc_macro_attribute]
dbus_proxy_obj(attr: TokenStream, item: TokenStream) -> TokenStream746 pub fn dbus_proxy_obj(attr: TokenStream, item: TokenStream) -> TokenStream {
747     let ori_item: proc_macro2::TokenStream = item.clone().into();
748 
749     let args = Punctuated::<Expr, Comma>::parse_separated_nonempty.parse(attr.clone()).unwrap();
750 
751     let struct_ident = if let Expr::Path(p) = &args[0] {
752         p.path.get_ident().unwrap()
753     } else {
754         panic!("struct name must be specified");
755     };
756 
757     let dbus_iface_name = if let Expr::Lit(lit) = &args[1] {
758         lit
759     } else {
760         panic!("D-Bus interface name must be specified");
761     };
762 
763     let mut method_impls = quote! {};
764 
765     let ast: ItemImpl = syn::parse(item.clone()).unwrap();
766     let self_ty = ast.self_ty;
767     let trait_ = ast.trait_.unwrap().1;
768 
769     for item in ast.items {
770         if let ImplItem::Method(method) = item {
771             // If the method is not marked with #[dbus_method], just copy the
772             // original method body.
773             if method.attrs.len() != 1 {
774                 method_impls = quote! {
775                     #method_impls
776                     #method
777                 };
778                 continue;
779             }
780 
781             let attr = &method.attrs[0];
782             if !attr.path.get_ident().unwrap().to_string().eq("dbus_method") {
783                 continue;
784             }
785 
786             let meta_list = match attr.parse_meta().unwrap() {
787                 Meta::List(meta_list) => meta_list,
788                 _ => continue,
789             };
790 
791             let dbus_method_name = meta_list.nested[0].clone();
792 
793             // logging is default to disabled if not specified
794             let dbus_logging = if meta_list.nested.len() > 1 {
795                 meta_list.nested[1].clone()
796             } else {
797                 let token = quote! { DBusLog::Disable };
798                 syn::parse2::<NestedMeta>(token).unwrap()
799             };
800 
801             let method_sig = method.sig.clone();
802 
803             let mut method_args = quote! {};
804             let mut args_debug = quote! {};
805             let mut args_debug_format = String::new();
806 
807             for input in method.sig.inputs {
808                 if let FnArg::Typed(ref typed) = input {
809                     let arg_type = &typed.ty;
810                     if let Pat::Ident(pat_ident) = &*typed.pat {
811                         let ident = pat_ident.ident.clone();
812 
813                         method_args = quote! {
814                             #method_args DBusArg::to_dbus(#ident).unwrap(),
815                         };
816 
817                         args_debug = quote! {
818                             #args_debug
819                             <#arg_type as DBusArg>::log(&#ident),
820                         };
821 
822                         if !args_debug_format.is_empty() {
823                             args_debug_format.push_str(", ");
824                         }
825                         args_debug_format.push_str("|{}|");
826                     }
827                 }
828             }
829 
830             let debug = quote! {
831                 let args_formatted = format!(#args_debug_format, #args_debug);
832                 DBusLog::log(#dbus_logging, "dbus out", #dbus_iface_name, #dbus_method_name, args_formatted.as_str());
833             };
834 
835             method_impls = quote! {
836                 #method_impls
837                 #[allow(unused_variables)]
838                 #method_sig {
839                     let remote__ = self.remote.clone();
840                     let objpath__ = self.objpath.clone();
841                     let conn__ = self.conn.clone();
842 
843                     #debug
844 
845                     let proxy = dbus::nonblock::Proxy::new(
846                         remote__,
847                         objpath__,
848                         std::time::Duration::from_secs(2),
849                         conn__,
850                     );
851                     let future: dbus::nonblock::MethodReply<()> = proxy.method_call(
852                         #dbus_iface_name,
853                         #dbus_method_name,
854                         (#method_args),
855                     );
856 
857                     // Acquire await lock before pushing task.
858                     let has_await_block = {
859                         let await_guard = self.futures_awaiting.lock().unwrap();
860                         self.cb_futures.lock().unwrap().push_back(future);
861                         *await_guard
862                     };
863 
864                     // Only insert async task if there isn't already one.
865                     if !has_await_block {
866                         // Callbacks will await in the order they were called.
867                         let futures = self.cb_futures.clone();
868                         let already_awaiting = self.futures_awaiting.clone();
869                         tokio::spawn(async move {
870                             // Check for another await block.
871                             {
872                                 let mut await_guard = already_awaiting.lock().unwrap();
873                                 if *await_guard {
874                                     return;
875                                 }
876 
877                                 // We are now the only awaiting block. Mark and
878                                 // drop the lock.
879                                 *await_guard = true;
880                             }
881 
882                             loop {
883                                 // Go through all pending futures and await them.
884                                 while futures.lock().unwrap().len() > 0 {
885                                     let future = {
886                                         let mut guard = futures.lock().unwrap();
887                                         match guard.pop_front() {
888                                             Some(f) => f,
889                                             None => {break;}
890                                         }
891                                     };
892                                     let _result = future.await;
893                                 }
894 
895                                 // Acquire await block and make final check on
896                                 // futures list to avoid racing against
897                                 // insertion. Must acquire in-order to avoid a
898                                 // deadlock.
899                                 {
900                                     let mut await_guard = already_awaiting.lock().unwrap();
901                                     let futures_guard = futures.lock().unwrap();
902                                     if (*futures_guard).len() > 0 {
903                                         continue;
904                                     }
905 
906                                     *await_guard = false;
907                                     break;
908                                 }
909                             }
910                         });
911                     }
912                 }
913             };
914         }
915     }
916 
917     let gen = quote! {
918         #ori_item
919 
920         impl RPCProxy for #self_ty {}
921 
922         struct #struct_ident {
923             conn: std::sync::Arc<dbus::nonblock::SyncConnection>,
924             remote: dbus::strings::BusName<'static>,
925             objpath: Path<'static>,
926             disconnect_watcher: std::sync::Arc<std::sync::Mutex<DisconnectWatcher>>,
927 
928             /// Callback futures to await. If accessing with |futures_awaiting|,
929             /// always acquire |futures_awaiting| first to avoid deadlock.
930             cb_futures: std::sync::Arc<std::sync::Mutex<std::collections::VecDeque<dbus::nonblock::MethodReply<()>>>>,
931 
932             /// Is there a task already awaiting on |cb_futures|? If acquiring
933             /// with |cb_futures|, always acquire this lock first to avoid deadlocks.
934             futures_awaiting: std::sync::Arc<std::sync::Mutex<bool>>,
935         }
936 
937         impl #struct_ident {
938             fn new(
939                 conn: std::sync::Arc<dbus::nonblock::SyncConnection>,
940                 remote: dbus::strings::BusName<'static>,
941                 objpath: Path<'static>,
942                 disconnect_watcher: std::sync::Arc<std::sync::Mutex<DisconnectWatcher>>) -> Self {
943                 Self {
944                     conn,
945                     remote,
946                     objpath,
947                     disconnect_watcher,
948                     cb_futures: std::sync::Arc::new(std::sync::Mutex::new(std::collections::VecDeque::new())),
949                     futures_awaiting: std::sync::Arc::new(std::sync::Mutex::new(false)),
950                 }
951             }
952         }
953 
954         impl #trait_ for #struct_ident {
955             #method_impls
956         }
957 
958         impl RPCProxy for #struct_ident {
959             fn register_disconnect(&mut self, disconnect_callback: Box<dyn Fn(u32) + Send>) -> u32 {
960                 return self.disconnect_watcher.lock().unwrap().add(self.remote.clone(), disconnect_callback);
961             }
962 
963             fn get_object_id(&self) -> String {
964                 self.objpath.to_string().clone()
965             }
966 
967             fn unregister(&mut self, id: u32) -> bool {
968                 self.disconnect_watcher.lock().unwrap().remove(self.remote.clone(), id)
969             }
970         }
971 
972         impl DBusArg for Box<dyn #trait_ + Send> {
973             type DBusType = Path<'static>;
974 
975             fn from_dbus(
976                 objpath__: Path<'static>,
977                 conn__: Option<std::sync::Arc<dbus::nonblock::SyncConnection>>,
978                 remote__: Option<dbus::strings::BusName<'static>>,
979                 disconnect_watcher__: Option<std::sync::Arc<std::sync::Mutex<DisconnectWatcher>>>,
980             ) -> Result<Box<dyn #trait_ + Send>, Box<dyn std::error::Error>> {
981                 Ok(Box::new(#struct_ident::new(
982                     conn__.unwrap(),
983                     remote__.unwrap(),
984                     objpath__,
985                     disconnect_watcher__.unwrap(),
986                 )))
987             }
988 
989             fn to_dbus(_data: Box<dyn #trait_ + Send>) -> Result<Path<'static>, Box<dyn std::error::Error>> {
990                 // This impl represents a remote DBus object, so `to_dbus` does not make sense.
991                 panic!("not implemented");
992             }
993 
994             fn log(_data: &Box<dyn #trait_ + Send>) -> String {
995                 format!("Box<dyn>")
996             }
997         }
998     };
999 
1000     debug_output_to_file(&gen, format!("out-{}.rs", struct_ident));
1001 
1002     gen.into()
1003 }
1004 
1005 /// Generates the definition of `DBusArg` trait required for D-Bus projection.
1006 ///
1007 /// Due to Rust orphan rule, `DBusArg` trait needs to be defined locally in the crate that wants to
1008 /// use D-Bus projection. Providing `DBusArg` as a public trait won't let other crates implement
1009 /// it for structs defined in foreign crates. As a workaround, this macro is provided to generate
1010 /// `DBusArg` trait definition.
1011 #[proc_macro]
generate_dbus_arg(_item: TokenStream) -> TokenStream1012 pub fn generate_dbus_arg(_item: TokenStream) -> TokenStream {
1013     let gen = quote! {
1014         use dbus::arg::RefArg;
1015         use dbus::nonblock::SyncConnection;
1016         use dbus::strings::BusName;
1017         use dbus_projection::DisconnectWatcher;
1018         use dbus_projection::impl_dbus_arg_from_into;
1019 
1020         use std::convert::{TryFrom, TryInto};
1021         use std::error::Error;
1022         use std::fmt;
1023         use std::hash::Hash;
1024         use std::sync::{Arc, Mutex};
1025 
1026         // Key for serialized Option<T> in propmap
1027         const OPTION_KEY: &'static str = "optional_value";
1028 
1029         #[derive(Debug)]
1030         pub(crate) struct DBusArgError {
1031             message: String,
1032         }
1033 
1034         impl DBusArgError {
1035             pub fn new(message: String) -> DBusArgError {
1036                 DBusArgError { message }
1037             }
1038         }
1039 
1040         impl fmt::Display for DBusArgError {
1041             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1042                 write!(f, "{}", self.message)
1043             }
1044         }
1045 
1046         impl Error for DBusArgError {}
1047 
1048         /// Trait for converting `dbus::arg::RefArg` to a Rust type.
1049         ///
1050         /// This trait needs to be implemented for all types that need to be
1051         /// converted from the D-Bus representation (`dbus::arg::RefArg`) to
1052         /// a Rust representation.
1053         ///
1054         /// These implementations should be provided as part of this macros
1055         /// library since the reference types are defined by the D-Bus specification
1056         /// (look under Basic Types, Container Types, etc) in
1057         /// https://dbus.freedesktop.org/doc/dbus-specification.html.
1058         pub(crate) trait RefArgToRust {
1059             type RustType;
1060             fn ref_arg_to_rust(
1061                 arg: &(dyn dbus::arg::RefArg + 'static),
1062                 name: String,
1063             ) -> Result<Self::RustType, Box<dyn Error>>;
1064         }
1065 
1066         impl<T: 'static + DirectDBus> RefArgToRust for T {
1067             type RustType = T;
1068             fn ref_arg_to_rust(
1069                 arg: &(dyn dbus::arg::RefArg + 'static),
1070                 name: String,
1071             ) -> Result<Self::RustType, Box<dyn Error>> {
1072                 let any = arg.as_any();
1073                 if !any.is::<<Self as DBusArg>::DBusType>() {
1074                     return Err(Box::new(DBusArgError::new(format!(
1075                         "{} type does not match: expected {}, found {}",
1076                         name,
1077                         std::any::type_name::<<Self as DBusArg>::DBusType>(),
1078                         arg.arg_type().as_str(),
1079                     ))));
1080                 }
1081                 let arg = (*any.downcast_ref::<<Self as DBusArg>::DBusType>().unwrap()).clone();
1082                 return Ok(arg);
1083             }
1084         }
1085 
1086         impl RefArgToRust for std::fs::File {
1087             type RustType = std::fs::File;
1088 
1089             fn ref_arg_to_rust(
1090                 arg: &(dyn dbus::arg::RefArg + 'static),
1091                 name: String,
1092             ) -> Result<Self::RustType, Box<dyn Error>> {
1093                 let any = arg.as_any();
1094                 if !any.is::<<Self as DBusArg>::DBusType>() {
1095                     return Err(Box::new(DBusArgError::new(format!(
1096                         "{} type does not match: expected {}, found {}",
1097                         name,
1098                         std::any::type_name::<<Self as DBusArg>::DBusType>(),
1099                         arg.arg_type().as_str(),
1100                     ))));
1101                 }
1102                 let arg = match (*any.downcast_ref::<<Self as DBusArg>::DBusType>().unwrap()).try_clone() {
1103                     Ok(foo) => foo,
1104                     Err(_) => return Err(Box::new(DBusArgError::new(format!("{} cannot clone file.", name)))),
1105                 };
1106 
1107                 return Ok(arg);
1108             }
1109         }
1110 
1111         impl RefArgToRust for dbus::arg::PropMap {
1112             type RustType = dbus::arg::PropMap;
1113             fn ref_arg_to_rust(
1114                 arg: &(dyn dbus::arg::RefArg + 'static),
1115                 name: String,
1116             ) -> Result<Self::RustType, Box<dyn Error>> {
1117                 let mut map: dbus::arg::PropMap = std::collections::HashMap::new();
1118                 let mut iter = match arg.as_iter() {
1119                     None => {
1120                         return Err(Box::new(DBusArgError::new(format!(
1121                             "{} is not iterable",
1122                             name,
1123                         ))))
1124                     }
1125                     Some(item) => item,
1126                 };
1127                 let mut key = iter.next();
1128                 let mut val = iter.next();
1129                 while !key.is_none() && !val.is_none() {
1130                     let k = key.unwrap().as_str().unwrap().to_string();
1131                     let val_clone = val.unwrap().box_clone();
1132                     let v = dbus::arg::Variant(
1133                         val_clone
1134                             .as_static_inner(0)
1135                             .ok_or(Box::new(DBusArgError::new(format!(
1136                                 "{}.{} is not a variant",
1137                                 name, k
1138                             ))))?
1139                             .box_clone(),
1140                     );
1141                     map.insert(k, v);
1142                     key = iter.next();
1143                     val = iter.next();
1144                 }
1145                 return Ok(map);
1146             }
1147         }
1148 
1149         // A vector is convertible from DBus' dynamic type RefArg to Rust's Vec, if the elements
1150         // of the vector are also convertible themselves recursively.
1151         impl<T: 'static + RefArgToRust<RustType = T>> RefArgToRust for Vec<T> {
1152             type RustType = Vec<T>;
1153             fn ref_arg_to_rust(
1154                 arg: &(dyn dbus::arg::RefArg + 'static),
1155                 _name: String,
1156             ) -> Result<Self::RustType, Box<dyn Error>> {
1157                 let mut vec: Vec<T> = vec![];
1158                 let mut iter = arg.as_iter().ok_or(Box::new(DBusArgError::new(format!(
1159                     "Failed parsing array for `{}`",
1160                     _name
1161                 ))))?;
1162                 let mut val = iter.next();
1163                 while !val.is_none() {
1164                     let arg = val.unwrap().box_clone();
1165                     let arg = <T as RefArgToRust>::ref_arg_to_rust(&arg, _name.clone() + " element")?;
1166                     vec.push(arg);
1167                     val = iter.next();
1168                 }
1169                 return Ok(vec);
1170             }
1171         }
1172 
1173         impl<
1174                 K: 'static + Eq + Hash + RefArgToRust<RustType = K>,
1175                 V: 'static + RefArgToRust<RustType = V>
1176             > RefArgToRust for std::collections::HashMap<K, V>
1177         {
1178             type RustType = std::collections::HashMap<K, V>;
1179 
1180             fn ref_arg_to_rust(
1181                 arg: &(dyn dbus::arg::RefArg + 'static),
1182                 name: String,
1183             ) -> Result<Self::RustType, Box<dyn Error>> {
1184                 let mut map: std::collections::HashMap<K, V> = std::collections::HashMap::new();
1185                 let mut iter = arg.as_iter().unwrap();
1186                 let mut key = iter.next();
1187                 let mut val = iter.next();
1188                 while !key.is_none() && !val.is_none() {
1189                     let k = key.unwrap().box_clone();
1190                     let k = <K as RefArgToRust>::ref_arg_to_rust(&k, name.clone() + " key")?;
1191                     let v = val.unwrap().box_clone();
1192                     let v = <V as RefArgToRust>::ref_arg_to_rust(&v, name.clone() + " value")?;
1193                     map.insert(k, v);
1194                     key = iter.next();
1195                     val = iter.next();
1196                 }
1197                 Ok(map)
1198             }
1199         }
1200 
1201         /// Trait describing how to convert to and from a D-Bus type, and to log D-Bus transaction.
1202         ///
1203         /// All Rust structs that need to be serialized to and from D-Bus need
1204         /// to implement this trait. Basic and container types will have their
1205         /// implementation provided by this macros crate.
1206         ///
1207         /// For Rust objects, implement the std::convert::TryFrom and std::convert::TryInto
1208         /// traits into the relevant basic or container types for serialization. A
1209         /// helper macro is provided in the `dbus_projection` macro (impl_dbus_arg_from_into).
1210         /// For enums, use `impl_dbus_arg_enum`.
1211         ///
1212         /// When implementing this trait for Rust container types (i.e. Option<T>),
1213         /// you must first select the D-Bus container type used (i.e. array, property map, etc) and
1214         /// then implement the `from_dbus`, `to_dbus`, and `log` functions.
1215         ///
1216         /// Note that when implementing `log` function for a container type, avoid using the "{:?}"
1217         /// Debug format because the `log` function could be recursively called and generate many
1218         /// backslashes.
1219         pub(crate) trait DBusArg {
1220             type DBusType;
1221 
1222             fn from_dbus(
1223                 x: Self::DBusType,
1224                 conn: Option<Arc<dbus::nonblock::SyncConnection>>,
1225                 remote: Option<BusName<'static>>,
1226                 disconnect_watcher: Option<Arc<Mutex<DisconnectWatcher>>>,
1227             ) -> Result<Self, Box<dyn Error>>
1228             where
1229                 Self: Sized;
1230 
1231             fn to_dbus(x: Self) -> Result<Self::DBusType, Box<dyn Error>>;
1232 
1233             fn log(x: &Self) -> String;
1234         }
1235 
1236         // Types that implement dbus::arg::Append do not need any conversion.
1237         pub(crate) trait DirectDBus: Clone + std::fmt::Debug {}
1238         impl DirectDBus for bool {}
1239         impl DirectDBus for i32 {}
1240         impl DirectDBus for u32 {}
1241         impl DirectDBus for i64 {}
1242         impl DirectDBus for u64 {}
1243         impl DirectDBus for f64 {}
1244         impl DirectDBus for i16 {}
1245         impl DirectDBus for u16 {}
1246         impl DirectDBus for u8 {}
1247         impl DirectDBus for String {}
1248         impl<T: DirectDBus> DBusArg for T {
1249             type DBusType = T;
1250 
1251             fn from_dbus(
1252                 data: T,
1253                 _conn: Option<Arc<dbus::nonblock::SyncConnection>>,
1254                 _remote: Option<BusName<'static>>,
1255                 _disconnect_watcher: Option<Arc<Mutex<DisconnectWatcher>>>,
1256             ) -> Result<T, Box<dyn Error>> {
1257                 return Ok(data);
1258             }
1259 
1260             fn to_dbus(data: T) -> Result<T, Box<dyn Error>> {
1261                 return Ok(data);
1262             }
1263 
1264             fn log(data: &T) -> String {
1265                 format!("{:?}", data)
1266             }
1267         }
1268 
1269         // Represent i8 as D-Bus's i16, since D-Bus only has unsigned type for BYTE.
1270         impl_dbus_arg_from_into!(i8, i16);
1271 
1272         impl DBusArg for std::fs::File {
1273             type DBusType = std::fs::File;
1274 
1275             fn from_dbus(
1276                 data: std::fs::File,
1277                 _conn: Option<Arc<dbus::nonblock::SyncConnection>>,
1278                 _remote: Option<BusName<'static>>,
1279                 _disconnect_watcher: Option<Arc<Mutex<DisconnectWatcher>>>,
1280             ) -> Result<std::fs::File, Box<dyn Error>> {
1281                 return Ok(data);
1282             }
1283 
1284             fn to_dbus(data: std::fs::File) -> Result<std::fs::File, Box<dyn Error>> {
1285                 return Ok(data);
1286             }
1287 
1288             fn log(data: &std::fs::File) -> String {
1289                 format!("{:?}", data)
1290             }
1291         }
1292 
1293         impl<T: DBusArg> DBusArg for Vec<T> {
1294             type DBusType = Vec<T::DBusType>;
1295 
1296             fn from_dbus(
1297                 data: Vec<T::DBusType>,
1298                 conn: Option<Arc<dbus::nonblock::SyncConnection>>,
1299                 remote: Option<BusName<'static>>,
1300                 disconnect_watcher: Option<Arc<Mutex<DisconnectWatcher>>>,
1301             ) -> Result<Vec<T>, Box<dyn Error>> {
1302                 let mut list: Vec<T> = vec![];
1303                 for prop in data {
1304                     let t = T::from_dbus(
1305                         prop,
1306                         conn.clone(),
1307                         remote.clone(),
1308                         disconnect_watcher.clone(),
1309                     )?;
1310                     list.push(t);
1311                 }
1312                 Ok(list)
1313             }
1314 
1315             fn to_dbus(data: Vec<T>) -> Result<Vec<T::DBusType>, Box<dyn Error>> {
1316                 let mut list: Vec<T::DBusType> = vec![];
1317                 for item in data {
1318                     let t = T::to_dbus(item)?;
1319                     list.push(t);
1320                 }
1321                 Ok(list)
1322             }
1323 
1324             fn log(data: &Vec<T>) -> String {
1325                 format!(
1326                     "[{}]",
1327                     data
1328                         .iter()
1329                         .map(|d| <T as DBusArg>::log(d))
1330                         .collect::<Vec<String>>()
1331                         .join(", ")
1332                 )
1333             }
1334         }
1335 
1336         impl<T: DBusArg> DBusArg for Option<T>
1337             where
1338                 <T as DBusArg>::DBusType: dbus::arg::RefArg
1339                     + 'static
1340                     + RefArgToRust<RustType = <T as DBusArg>::DBusType>,
1341         {
1342             type DBusType = dbus::arg::PropMap;
1343 
1344             fn from_dbus(
1345                 data: dbus::arg::PropMap,
1346                 conn: Option<Arc<dbus::nonblock::SyncConnection>>,
1347                 remote: Option<BusName<'static>>,
1348                 disconnect_watcher: Option<Arc<Mutex<DisconnectWatcher>>>)
1349                 -> Result<Option<T>, Box<dyn Error>> {
1350 
1351                 // It's Ok if the key doesn't exist. That just means we have an empty option (i.e.
1352                 // None).
1353                 let prop_value = match data.get(OPTION_KEY) {
1354                     Some(data) => data,
1355                     None => {
1356                         return Ok(None);
1357                     }
1358                 };
1359 
1360                 // Make sure the option type was encoded correctly. If the key exists but the value
1361                 // is not right, we return an Err type.
1362                 match prop_value.arg_type() {
1363                     dbus::arg::ArgType::Variant => (),
1364                     _ => {
1365                         return Err(Box::new(DBusArgError::new(format!("{} must be a variant", OPTION_KEY))));
1366                     }
1367                 };
1368 
1369                 // Convert the Variant into the target type and return an Err if that fails.
1370                 let ref_value: <T as DBusArg>::DBusType = match <<T as DBusArg>::DBusType as RefArgToRust>::ref_arg_to_rust(
1371                     prop_value.as_static_inner(0).unwrap(),
1372                     OPTION_KEY.to_string()) {
1373                     Ok(v) => v,
1374                     Err(e) => return Err(e),
1375                 };
1376 
1377                 let value = match T::from_dbus(ref_value, conn, remote, disconnect_watcher) {
1378                     Ok(v) => Some(v),
1379                     Err(e) => return Err(e),
1380                 };
1381 
1382                 Ok(value)
1383             }
1384 
1385             fn to_dbus(data: Option<T>) -> Result<dbus::arg::PropMap, Box<dyn Error>> {
1386                 let mut props = dbus::arg::PropMap::new();
1387 
1388                 if let Some(d) = data {
1389                     let b = T::to_dbus(d)?;
1390                     props.insert(OPTION_KEY.to_string(), dbus::arg::Variant(Box::new(b)));
1391                 }
1392 
1393                 Ok(props)
1394             }
1395 
1396             fn log(data: &Option<T>) -> String {
1397                 if let Some(d) = data.as_ref() {
1398                     format!("Some({})", <T as DBusArg>::log(d))
1399                 } else {
1400                     String::from("None")
1401                 }
1402             }
1403         }
1404 
1405         impl<K: Eq + Hash + DBusArg, V: DBusArg> DBusArg for std::collections::HashMap<K, V>
1406             where
1407                 <K as DBusArg>::DBusType: 'static
1408                     + Eq
1409                     + Hash
1410                     + dbus::arg::RefArg
1411                     + RefArgToRust<RustType = <K as DBusArg>::DBusType>,
1412         {
1413             type DBusType = std::collections::HashMap<K::DBusType, V::DBusType>;
1414 
1415             fn from_dbus(
1416                 data: std::collections::HashMap<K::DBusType, V::DBusType>,
1417                 conn: Option<std::sync::Arc<dbus::nonblock::SyncConnection>>,
1418                 remote: Option<dbus::strings::BusName<'static>>,
1419                 disconnect_watcher: Option<
1420                     std::sync::Arc<std::sync::Mutex<dbus_projection::DisconnectWatcher>>>,
1421             ) -> Result<std::collections::HashMap<K, V>, Box<dyn std::error::Error>> {
1422                 let mut map = std::collections::HashMap::new();
1423                 for (key, val) in data {
1424                     let k = K::from_dbus(
1425                         key,
1426                         conn.clone(),
1427                         remote.clone(),
1428                         disconnect_watcher.clone()
1429                     )?;
1430                     let v = V::from_dbus(
1431                         val,
1432                         conn.clone(),
1433                         remote.clone(),
1434                         disconnect_watcher.clone()
1435                     )?;
1436                     map.insert(k, v);
1437                 }
1438                 Ok(map)
1439             }
1440 
1441             fn to_dbus(
1442                 data: std::collections::HashMap<K, V>,
1443             ) -> Result<std::collections::HashMap<K::DBusType, V::DBusType>, Box<dyn std::error::Error>>
1444             {
1445                 let mut map = std::collections::HashMap::new();
1446                 for (key, val) in data {
1447                     let k = K::to_dbus(key)?;
1448                     let v = V::to_dbus(val)?;
1449                     map.insert(k, v);
1450                 }
1451                 Ok(map)
1452             }
1453 
1454             fn log(data: &std::collections::HashMap<K, V>) -> String {
1455                 format!(
1456                     "{{{}}}",
1457                     data.iter()
1458                         .map(|(k, v)| format!(
1459                             "{}: {}",
1460                             <K as DBusArg>::log(k),
1461                             <V as DBusArg>::log(v),
1462                         ))
1463                         .collect::<Vec<String>>()
1464                         .join(", ")
1465                 )
1466             }
1467         }
1468     };
1469 
1470     debug_output_to_file(&gen, "out-generate_dbus_arg.rs".into());
1471 
1472     gen.into()
1473 }
1474