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