• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 extern crate proc_macro;
2 
3 use quote::{format_ident, quote, ToTokens};
4 
5 use std::fs::File;
6 use std::io::Write;
7 use std::path::Path;
8 
9 use syn::parse::Parser;
10 use syn::punctuated::Punctuated;
11 use syn::token::Comma;
12 use syn::{Expr, FnArg, ImplItem, ItemImpl, ItemStruct, Meta, Pat, ReturnType, Type};
13 
14 use crate::proc_macro::TokenStream;
15 
16 const OUTPUT_DEBUG: bool = false;
17 
debug_output_to_file(gen: &proc_macro2::TokenStream, filename: String)18 fn debug_output_to_file(gen: &proc_macro2::TokenStream, filename: String) {
19     if !OUTPUT_DEBUG {
20         return;
21     }
22 
23     let filepath = std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap())
24         .join(filename)
25         .to_str()
26         .unwrap()
27         .to_string();
28 
29     let path = Path::new(&filepath);
30     let mut file = File::create(&path).unwrap();
31     file.write_all(gen.to_string().as_bytes()).unwrap();
32 }
33 
34 /// Marks a method to be projected to a D-Bus method and specifies the D-Bus method name.
35 #[proc_macro_attribute]
dbus_method(_attr: TokenStream, item: TokenStream) -> TokenStream36 pub fn dbus_method(_attr: TokenStream, item: TokenStream) -> TokenStream {
37     let ori_item: proc_macro2::TokenStream = item.clone().into();
38     let gen = quote! {
39         #[allow(unused_variables)]
40         #ori_item
41     };
42     gen.into()
43 }
44 
45 /// Generates a function to export a Rust object to D-Bus.
46 ///
47 /// Example:
48 ///   `#[generate_dbus_exporter(export_foo_dbus_obj, "org.example.FooInterface")]`
49 ///
50 /// This generates a method called `export_foo_dbus_obj` that will export a Rust object into a
51 /// D-Bus object having interface `org.example.FooInterface`.
52 #[proc_macro_attribute]
generate_dbus_exporter(attr: TokenStream, item: TokenStream) -> TokenStream53 pub fn generate_dbus_exporter(attr: TokenStream, item: TokenStream) -> TokenStream {
54     let ori_item: proc_macro2::TokenStream = item.clone().into();
55 
56     let args = Punctuated::<Expr, Comma>::parse_separated_nonempty.parse(attr.clone()).unwrap();
57 
58     let fn_ident = if let Expr::Path(p) = &args[0] {
59         p.path.get_ident().unwrap()
60     } else {
61         panic!("function name must be specified");
62     };
63 
64     let dbus_iface_name = if let Expr::Lit(lit) = &args[1] {
65         lit
66     } else {
67         panic!("D-Bus interface name must be specified");
68     };
69 
70     let ast: ItemImpl = syn::parse(item.clone()).unwrap();
71     let api_iface_ident = ast.trait_.unwrap().1.to_token_stream();
72 
73     let mut register_methods = quote! {};
74 
75     let obj_type = quote! { std::sync::Arc<std::sync::Mutex<Box<T>>> };
76 
77     for item in ast.items {
78         if let ImplItem::Method(method) = item {
79             if method.attrs.len() != 1 {
80                 continue;
81             }
82 
83             let attr = &method.attrs[0];
84             if !attr.path.get_ident().unwrap().to_string().eq("dbus_method") {
85                 continue;
86             }
87 
88             let attr_args = attr.parse_meta().unwrap();
89             let dbus_method_name = if let Meta::List(meta_list) = attr_args {
90                 Some(meta_list.nested[0].clone())
91             } else {
92                 None
93             };
94 
95             if dbus_method_name.is_none() {
96                 continue;
97             }
98 
99             let method_name = method.sig.ident;
100 
101             let mut arg_names = quote! {};
102             let mut method_args = quote! {};
103             let mut make_args = quote! {};
104             let mut dbus_input_vars = quote! {};
105             let mut dbus_input_types = quote! {};
106 
107             for input in method.sig.inputs {
108                 if let FnArg::Typed(ref typed) = input {
109                     let arg_type = &typed.ty;
110                     if let Pat::Ident(pat_ident) = &*typed.pat {
111                         let ident = pat_ident.ident.clone();
112                         let mut dbus_input_ident = ident.to_string();
113                         dbus_input_ident.push_str("_");
114                         let dbus_input_arg = format_ident!("{}", dbus_input_ident);
115                         let ident_string = ident.to_string();
116 
117                         arg_names = quote! {
118                             #arg_names #ident_string,
119                         };
120 
121                         method_args = quote! {
122                             #method_args #ident,
123                         };
124 
125                         dbus_input_vars = quote! {
126                             #dbus_input_vars #dbus_input_arg,
127                         };
128 
129                         dbus_input_types = quote! {
130                             #dbus_input_types
131                             <#arg_type as DBusArg>::DBusType,
132                         };
133 
134                         make_args = quote! {
135                             #make_args
136                             let #ident = <#arg_type as DBusArg>::from_dbus(
137                                 #dbus_input_arg,
138                                 Some(conn_clone.clone()),
139                                 Some(ctx.message().sender().unwrap().into_static()),
140                                 Some(dc_watcher_clone.clone()),
141                             );
142 
143                             if let Result::Err(e) = #ident {
144                                 return Err(dbus_crossroads::MethodErr::invalid_arg(
145                                     e.to_string().as_str()
146                                 ));
147                             }
148 
149                             let #ident = #ident.unwrap();
150                         };
151                     }
152                 }
153             }
154 
155             let dbus_input_args = quote! {
156                 (#dbus_input_vars): (#dbus_input_types)
157             };
158 
159             let mut output_names = quote! {};
160             let mut output_type = quote! {};
161             let mut ret = quote! {Ok(())};
162             if let ReturnType::Type(_, t) = method.sig.output {
163                 output_type = quote! {<#t as DBusArg>::DBusType,};
164                 ret = quote! {Ok((<#t as DBusArg>::to_dbus(ret).unwrap(),))};
165                 output_names = quote! { "out", };
166             }
167 
168             register_methods = quote! {
169                 #register_methods
170 
171                 let conn_clone = conn.clone();
172                 let dc_watcher_clone = disconnect_watcher.clone();
173                 let handle_method = move |ctx: &mut dbus_crossroads::Context,
174                                           obj: &mut #obj_type,
175                                           #dbus_input_args |
176                       -> Result<(#output_type), dbus_crossroads::MethodErr> {
177                     #make_args
178                     let ret = obj.lock().unwrap().#method_name(#method_args);
179                     #ret
180                 };
181                 ibuilder.method(
182                     #dbus_method_name,
183                     (#arg_names),
184                     (#output_names),
185                     handle_method,
186                 );
187             };
188         }
189     }
190 
191     let gen = quote! {
192         #ori_item
193 
194         pub fn #fn_ident<T: 'static + #api_iface_ident + Send + ?Sized, P: Into<dbus::Path<'static>>>(
195             path: P,
196             conn: std::sync::Arc<dbus::nonblock::SyncConnection>,
197             cr: &mut dbus_crossroads::Crossroads,
198             obj: #obj_type,
199             disconnect_watcher: std::sync::Arc<std::sync::Mutex<dbus_projection::DisconnectWatcher>>,
200         ) {
201             fn get_iface_token<T: #api_iface_ident + Send + ?Sized>(
202                 conn: std::sync::Arc<dbus::nonblock::SyncConnection>,
203                 cr: &mut dbus_crossroads::Crossroads,
204                 disconnect_watcher: std::sync::Arc<std::sync::Mutex<dbus_projection::DisconnectWatcher>>,
205             ) -> dbus_crossroads::IfaceToken<#obj_type> {
206                 cr.register(#dbus_iface_name, |ibuilder| {
207                     #register_methods
208                 })
209             }
210 
211             let iface_token = get_iface_token(conn, cr, disconnect_watcher);
212             cr.insert(path, &[iface_token], obj);
213         }
214     };
215 
216     debug_output_to_file(&gen, format!("out-{}.rs", fn_ident.to_string()));
217 
218     gen.into()
219 }
220 
221 /// Generates a client implementation of a D-Bus interface.
222 ///
223 /// Example:
224 ///   #[generate_dbus_interface_client()]
225 ///
226 /// The impl containing #[dbus_method()] will contain a generated code to call the method via D-Bus.
227 #[proc_macro_attribute]
generate_dbus_interface_client(_attr: TokenStream, item: TokenStream) -> TokenStream228 pub fn generate_dbus_interface_client(_attr: TokenStream, item: TokenStream) -> TokenStream {
229     let ast: ItemImpl = syn::parse(item.clone()).unwrap();
230     let trait_path = ast.trait_.unwrap().1;
231     let struct_path = match *ast.self_ty {
232         Type::Path(path) => path,
233         _ => panic!("Struct path not available"),
234     };
235 
236     let mut methods = quote! {};
237 
238     // Iterate on every methods of a trait impl
239     for item in ast.items {
240         if let ImplItem::Method(method) = item {
241             // If the method is not marked with #[dbus_method], just copy the
242             // original method body.
243             if method.attrs.len() != 1 {
244                 methods = quote! {
245                     #methods
246 
247                     #method
248                 };
249                 continue;
250             }
251 
252             let attr = &method.attrs[0];
253             if !attr.path.get_ident().unwrap().to_string().eq("dbus_method") {
254                 continue;
255             }
256 
257             let sig = &method.sig;
258 
259             let dbus_method_name = if let Meta::List(meta_list) = attr.parse_meta().unwrap() {
260                 Some(meta_list.nested[0].clone())
261             } else {
262                 None
263             };
264 
265             if dbus_method_name.is_none() {
266                 continue;
267             }
268 
269             let mut input_list = quote! {};
270 
271             let mut object_conversions = quote! {};
272 
273             // Iterate on every parameter of a method to build a tuple, e.g.
274             // `(param1, param2, param3)`
275             for input in &method.sig.inputs {
276                 if let FnArg::Typed(ref typed) = input {
277                     let arg_type = &typed.ty;
278                     if let Pat::Ident(pat_ident) = &*typed.pat {
279                         let ident = pat_ident.ident.clone();
280 
281                         let is_box = if let Type::Path(type_path) = &**arg_type {
282                             if type_path.path.segments[0].ident.to_string().eq("Box") {
283                                 true
284                             } else {
285                                 false
286                             }
287                         } else {
288                             false
289                         };
290 
291                         if is_box {
292                             // A Box<dyn> parameter means this is an object that should be exported
293                             // on D-Bus.
294                             object_conversions = quote! {
295                                 #object_conversions
296                                     let #ident = {
297                                         let path = dbus::Path::new(#ident.get_object_id()).unwrap();
298                                         #ident.export_for_rpc();
299                                         path
300                                     };
301                             };
302 
303                             input_list = quote! {
304                                 #input_list
305                                 #ident,
306                             };
307                         } else {
308                             // Convert every parameter to its corresponding type recognized by
309                             // the D-Bus library.
310                             input_list = quote! {
311                                 #input_list
312                                 <#arg_type as DBusArg>::to_dbus(#ident).unwrap(),
313                             };
314                         }
315                     }
316                 }
317             }
318 
319             let mut output_as_dbus_arg = quote! {};
320             if let ReturnType::Type(_, t) = &method.sig.output {
321                 output_as_dbus_arg = quote! {<#t as DBusArg>};
322             }
323 
324             let input_tuple = quote! {
325                 (#input_list)
326             };
327 
328             let body = match &method.sig.output {
329                 // Build the method call to `self.client_proxy`. `method` or `method_noreturn`
330                 // depends on whether there is a return from the function.
331                 ReturnType::Default => {
332                     quote! {
333                         self.client_proxy.method_noreturn(#dbus_method_name, #input_tuple)
334                     }
335                 }
336                 _ => {
337                     quote! {
338                         let ret: #output_as_dbus_arg::DBusType = self.client_proxy.method(
339                             #dbus_method_name,
340                             #input_tuple,
341                         );
342                         #output_as_dbus_arg::from_dbus(ret, None, None, None).unwrap()
343                     }
344                 }
345             };
346 
347             // Assemble the method body. May have object conversions if there is a param that is
348             // a proxy object (`Box<dyn>` type).
349             let body = quote! {
350                 #object_conversions
351 
352                 #body
353             };
354 
355             // The method definition is its signature and the body.
356             let generated_method = quote! {
357                 #sig {
358                     #body
359                 }
360             };
361 
362             // Assemble all the method definitions.
363             methods = quote! {
364                 #methods
365 
366                 #generated_method
367             };
368         }
369     }
370 
371     let gen = quote! {
372         impl #trait_path for #struct_path {
373             #methods
374         }
375     };
376 
377     debug_output_to_file(
378         &gen,
379         std::path::PathBuf::from(std::env::var("OUT_DIR").unwrap())
380             .join(format!("out-{}.rs", struct_path.path.get_ident().unwrap()))
381             .to_str()
382             .unwrap()
383             .to_string(),
384     );
385 
386     gen.into()
387 }
388 
copy_without_attributes(item: &TokenStream) -> TokenStream389 fn copy_without_attributes(item: &TokenStream) -> TokenStream {
390     let mut ast: ItemStruct = syn::parse(item.clone()).unwrap();
391     for field in &mut ast.fields {
392         field.attrs.clear();
393     }
394 
395     let gen = quote! {
396         #ast
397     };
398 
399     gen.into()
400 }
401 
402 /// Generates a DBusArg implementation to transform Rust plain structs to a D-Bus data structure.
403 // TODO: Support more data types of struct fields (currently only supports integers and enums).
404 #[proc_macro_attribute]
dbus_propmap(attr: TokenStream, item: TokenStream) -> TokenStream405 pub fn dbus_propmap(attr: TokenStream, item: TokenStream) -> TokenStream {
406     let ori_item: proc_macro2::TokenStream = copy_without_attributes(&item).into();
407 
408     let ast: ItemStruct = syn::parse(item.clone()).unwrap();
409 
410     let args = Punctuated::<Expr, Comma>::parse_separated_nonempty.parse(attr.clone()).unwrap();
411     let struct_ident =
412         if let Expr::Path(p) = &args[0] { p.path.get_ident().unwrap().clone() } else { ast.ident };
413 
414     let struct_str = struct_ident.to_string();
415 
416     let mut make_fields = quote! {};
417     let mut field_idents = quote! {};
418 
419     let mut insert_map_fields = quote! {};
420     for field in ast.fields {
421         let field_ident = field.ident;
422 
423         if field_ident.is_none() {
424             continue;
425         }
426 
427         let field_str = field_ident.as_ref().unwrap().clone().to_string();
428 
429         let field_type = if let Type::Path(t) = field.ty {
430             t
431         } else {
432             continue;
433         };
434 
435         field_idents = quote! {
436             #field_idents #field_ident,
437         };
438 
439         let field_type_name = format_ident! {"{}_type_", field_str};
440         let make_field = quote! {
441             match #field_ident.arg_type() {
442                 dbus::arg::ArgType::Variant => {}
443                 _ => {
444                     return Err(Box::new(DBusArgError::new(String::from(format!(
445                         "{}.{} must be a variant",
446                         #struct_str, #field_str
447                     )))));
448                 }
449             };
450             let #field_ident = <<#field_type as DBusArg>::DBusType as RefArgToRust>::ref_arg_to_rust(
451                 #field_ident.as_static_inner(0).unwrap(),
452                 format!("{}.{}", #struct_str, #field_str),
453             )?;
454             type #field_type_name = #field_type;
455             let #field_ident = #field_type_name::from_dbus(
456                 #field_ident,
457                 conn__.clone(),
458                 remote__.clone(),
459                 disconnect_watcher__.clone(),
460             )?;
461         };
462 
463         make_fields = quote! {
464             #make_fields
465 
466             let #field_ident = match data__.get(#field_str) {
467                 Some(data) => data,
468                 None => {
469                     return Err(Box::new(DBusArgError::new(String::from(format!(
470                         "{}.{} is required",
471                         #struct_str, #field_str
472                     )))));
473                 }
474             };
475             #make_field
476         };
477 
478         insert_map_fields = quote! {
479             #insert_map_fields
480             let field_data__ = DBusArg::to_dbus(data__.#field_ident)?;
481             map__.insert(String::from(#field_str), dbus::arg::Variant(Box::new(field_data__)));
482         };
483     }
484 
485     let gen = quote! {
486         #[allow(dead_code)]
487         #ori_item
488 
489         impl DBusArg for #struct_ident {
490             type DBusType = dbus::arg::PropMap;
491 
492             fn from_dbus(
493                 data__: dbus::arg::PropMap,
494                 conn__: Option<std::sync::Arc<dbus::nonblock::SyncConnection>>,
495                 remote__: Option<dbus::strings::BusName<'static>>,
496                 disconnect_watcher__: Option<std::sync::Arc<std::sync::Mutex<dbus_projection::DisconnectWatcher>>>,
497             ) -> Result<#struct_ident, Box<dyn std::error::Error>> {
498                 #make_fields
499 
500                 return Ok(#struct_ident {
501                     #field_idents
502                     ..Default::default()
503                 });
504             }
505 
506             fn to_dbus(data__: #struct_ident) -> Result<dbus::arg::PropMap, Box<dyn std::error::Error>> {
507                 let mut map__: dbus::arg::PropMap = std::collections::HashMap::new();
508                 #insert_map_fields
509                 return Ok(map__);
510             }
511         }
512     };
513 
514     debug_output_to_file(&gen, format!("out-{}.rs", struct_ident.to_string()));
515 
516     gen.into()
517 }
518 
519 /// Generates a DBusArg implementation of a Remote RPC proxy object.
520 #[proc_macro_attribute]
dbus_proxy_obj(attr: TokenStream, item: TokenStream) -> TokenStream521 pub fn dbus_proxy_obj(attr: TokenStream, item: TokenStream) -> TokenStream {
522     let ori_item: proc_macro2::TokenStream = item.clone().into();
523 
524     let args = Punctuated::<Expr, Comma>::parse_separated_nonempty.parse(attr.clone()).unwrap();
525 
526     let struct_ident = if let Expr::Path(p) = &args[0] {
527         p.path.get_ident().unwrap()
528     } else {
529         panic!("struct name must be specified");
530     };
531 
532     let dbus_iface_name = if let Expr::Lit(lit) = &args[1] {
533         lit
534     } else {
535         panic!("D-Bus interface name must be specified");
536     };
537 
538     let mut method_impls = quote! {};
539 
540     let ast: ItemImpl = syn::parse(item.clone()).unwrap();
541     let self_ty = ast.self_ty;
542     let trait_ = ast.trait_.unwrap().1;
543 
544     for item in ast.items {
545         if let ImplItem::Method(method) = item {
546             // If the method is not marked with #[dbus_method], just copy the
547             // original method body.
548             if method.attrs.len() != 1 {
549                 method_impls = quote! {
550                     #method_impls
551                     #method
552                 };
553                 continue;
554             }
555 
556             let attr = &method.attrs[0];
557             if !attr.path.get_ident().unwrap().to_string().eq("dbus_method") {
558                 continue;
559             }
560 
561             let attr_args = attr.parse_meta().unwrap();
562             let dbus_method_name = if let Meta::List(meta_list) = attr_args {
563                 Some(meta_list.nested[0].clone())
564             } else {
565                 None
566             };
567 
568             if dbus_method_name.is_none() {
569                 continue;
570             }
571 
572             let method_sig = method.sig.clone();
573 
574             let mut method_args = quote! {};
575 
576             for input in method.sig.inputs {
577                 if let FnArg::Typed(ref typed) = input {
578                     if let Pat::Ident(pat_ident) = &*typed.pat {
579                         let ident = pat_ident.ident.clone();
580 
581                         method_args = quote! {
582                             #method_args DBusArg::to_dbus(#ident).unwrap(),
583                         };
584                     }
585                 }
586             }
587 
588             method_impls = quote! {
589                 #method_impls
590                 #[allow(unused_variables)]
591                 #method_sig {
592                     let remote__ = self.remote.clone();
593                     let objpath__ = self.objpath.clone();
594                     let conn__ = self.conn.clone();
595                     tokio::spawn(async move {
596                         let proxy = dbus::nonblock::Proxy::new(
597                             remote__,
598                             objpath__,
599                             std::time::Duration::from_secs(2),
600                             conn__,
601                         );
602                         let future: dbus::nonblock::MethodReply<()> = proxy.method_call(
603                             #dbus_iface_name,
604                             #dbus_method_name,
605                             (#method_args),
606                         );
607                         let _result = future.await;
608                     });
609                 }
610             };
611         }
612     }
613 
614     let gen = quote! {
615         #ori_item
616 
617         impl RPCProxy for #self_ty {
618             fn register_disconnect(&mut self, _disconnect_callback: Box<dyn Fn(u32) + Send>) -> u32 { 0 }
619             fn get_object_id(&self) -> String {
620                 String::from("")
621             }
622             fn unregister(&mut self, _id: u32) -> bool { false }
623             fn export_for_rpc(self: Box<Self>) {}
624         }
625 
626         struct #struct_ident {
627             conn: std::sync::Arc<dbus::nonblock::SyncConnection>,
628             remote: dbus::strings::BusName<'static>,
629             objpath: Path<'static>,
630             disconnect_watcher: std::sync::Arc<std::sync::Mutex<DisconnectWatcher>>,
631         }
632 
633         impl #trait_ for #struct_ident {
634             #method_impls
635         }
636 
637         impl RPCProxy for #struct_ident {
638             fn register_disconnect(&mut self, disconnect_callback: Box<dyn Fn(u32) + Send>) -> u32 {
639                 return self.disconnect_watcher.lock().unwrap().add(self.remote.clone(), disconnect_callback);
640             }
641 
642             fn get_object_id(&self) -> String {
643                 self.objpath.to_string().clone()
644             }
645 
646             fn unregister(&mut self, id: u32) -> bool {
647                 self.disconnect_watcher.lock().unwrap().remove(self.remote.clone(), id)
648             }
649             fn export_for_rpc(self: Box<Self>) {}
650         }
651 
652         impl DBusArg for Box<dyn #trait_ + Send> {
653             type DBusType = Path<'static>;
654 
655             fn from_dbus(
656                 objpath__: Path<'static>,
657                 conn__: Option<std::sync::Arc<dbus::nonblock::SyncConnection>>,
658                 remote__: Option<dbus::strings::BusName<'static>>,
659                 disconnect_watcher__: Option<std::sync::Arc<std::sync::Mutex<DisconnectWatcher>>>,
660             ) -> Result<Box<dyn #trait_ + Send>, Box<dyn std::error::Error>> {
661                 Ok(Box::new(#struct_ident {
662                     conn: conn__.unwrap(),
663                     remote: remote__.unwrap(),
664                     objpath: objpath__,
665                     disconnect_watcher: disconnect_watcher__.unwrap(),
666                 }))
667             }
668 
669             fn to_dbus(_data: Box<dyn #trait_ + Send>) -> Result<Path<'static>, Box<dyn std::error::Error>> {
670                 // This impl represents a remote DBus object, so `to_dbus` does not make sense.
671                 panic!("not implemented");
672             }
673         }
674     };
675 
676     debug_output_to_file(&gen, format!("out-{}.rs", struct_ident.to_string()));
677 
678     gen.into()
679 }
680 
681 /// Generates the definition of `DBusArg` trait required for D-Bus projection.
682 ///
683 /// Due to Rust orphan rule, `DBusArg` trait needs to be defined locally in the crate that wants to
684 /// use D-Bus projection. Providing `DBusArg` as a public trait won't let other crates implement
685 /// it for structs defined in foreign crates. As a workaround, this macro is provided to generate
686 /// `DBusArg` trait definition.
687 #[proc_macro]
generate_dbus_arg(_item: TokenStream) -> TokenStream688 pub fn generate_dbus_arg(_item: TokenStream) -> TokenStream {
689     let gen = quote! {
690         use dbus::arg::PropMap;
691         use dbus::nonblock::SyncConnection;
692         use dbus::strings::BusName;
693         use dbus_projection::DisconnectWatcher;
694 
695         use std::error::Error;
696         use std::fmt;
697         use std::sync::{Arc, Mutex};
698 
699         #[derive(Debug)]
700         pub(crate) struct DBusArgError {
701             message: String,
702         }
703 
704         impl DBusArgError {
705             pub fn new(message: String) -> DBusArgError {
706                 DBusArgError { message }
707             }
708         }
709 
710         impl fmt::Display for DBusArgError {
711             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
712                 write!(f, "{}", self.message)
713             }
714         }
715 
716         impl Error for DBusArgError {}
717 
718         pub(crate) trait RefArgToRust {
719             type RustType;
720             fn ref_arg_to_rust(
721                 arg: &(dyn dbus::arg::RefArg + 'static),
722                 name: String,
723             ) -> Result<Self::RustType, Box<dyn Error>>;
724         }
725 
726         impl<T: 'static + Clone + DirectDBus> RefArgToRust for T {
727             type RustType = T;
728             fn ref_arg_to_rust(
729                 arg: &(dyn dbus::arg::RefArg + 'static),
730                 name: String,
731             ) -> Result<Self::RustType, Box<dyn Error>> {
732                 let any = arg.as_any();
733                 if !any.is::<<Self as DBusArg>::DBusType>() {
734                     return Err(Box::new(DBusArgError::new(String::from(format!(
735                         "{} type does not match: expected {}, found {}",
736                         name,
737                         std::any::type_name::<<Self as DBusArg>::DBusType>(),
738                         arg.arg_type().as_str(),
739                     )))));
740                 }
741                 let arg = (*any.downcast_ref::<<Self as DBusArg>::DBusType>().unwrap()).clone();
742                 return Ok(arg);
743             }
744         }
745 
746         impl RefArgToRust for dbus::arg::PropMap {
747             type RustType = dbus::arg::PropMap;
748             fn ref_arg_to_rust(
749                 arg: &(dyn dbus::arg::RefArg + 'static),
750                 name: String,
751             ) -> Result<Self::RustType, Box<dyn Error>> {
752                 let mut map: dbus::arg::PropMap = std::collections::HashMap::new();
753                 let mut iter = match arg.as_iter() {
754                     None => {
755                         return Err(Box::new(DBusArgError::new(String::from(format!(
756                             "{} is not iterable",
757                             name,
758                         )))))
759                     }
760                     Some(item) => item,
761                 };
762                 let mut key = iter.next();
763                 let mut val = iter.next();
764                 while !key.is_none() && !val.is_none() {
765                     let k = key.unwrap().as_str().unwrap().to_string();
766                     let v = dbus::arg::Variant(val.unwrap().box_clone());
767                     map.insert(k, v);
768                     key = iter.next();
769                     val = iter.next();
770                 }
771                 return Ok(map);
772             }
773         }
774 
775         // A vector is convertible from DBus' dynamic type RefArg to Rust's Vec, if the elements
776         // of the vector are also convertible themselves recursively.
777         impl<T: 'static + RefArgToRust<RustType = T>> RefArgToRust for Vec<T> {
778             type RustType = Vec<T>;
779             fn ref_arg_to_rust(
780                 arg: &(dyn dbus::arg::RefArg + 'static),
781                 _name: String,
782             ) -> Result<Self::RustType, Box<dyn Error>> {
783                 let mut vec: Vec<T> = vec![];
784                 let mut iter = arg.as_iter().unwrap();
785                 let mut val = iter.next();
786                 while !val.is_none() {
787                     let arg = val.unwrap().box_clone();
788                     let arg = <T as RefArgToRust>::ref_arg_to_rust(&arg, _name.clone() + " element")?;
789                     vec.push(arg);
790                     val = iter.next();
791                 }
792                 return Ok(vec);
793             }
794         }
795 
796         pub(crate) trait DBusArg {
797             type DBusType;
798 
799             fn from_dbus(
800                 x: Self::DBusType,
801                 conn: Option<Arc<dbus::nonblock::SyncConnection>>,
802                 remote: Option<BusName<'static>>,
803                 disconnect_watcher: Option<Arc<Mutex<DisconnectWatcher>>>,
804             ) -> Result<Self, Box<dyn Error>>
805             where
806                 Self: Sized;
807 
808             fn to_dbus(x: Self) -> Result<Self::DBusType, Box<dyn Error>>;
809         }
810 
811         // Types that implement dbus::arg::Append do not need any conversion.
812         pub(crate) trait DirectDBus {}
813         impl DirectDBus for bool {}
814         impl DirectDBus for i32 {}
815         impl DirectDBus for u32 {}
816         impl DirectDBus for i64 {}
817         impl DirectDBus for u64 {}
818         impl DirectDBus for u16 {}
819         impl DirectDBus for u8 {}
820         impl DirectDBus for String {}
821         impl<T: DirectDBus> DBusArg for T {
822             type DBusType = T;
823 
824             fn from_dbus(
825                 data: T,
826                 _conn: Option<Arc<dbus::nonblock::SyncConnection>>,
827                 _remote: Option<BusName<'static>>,
828                 _disconnect_watcher: Option<Arc<Mutex<DisconnectWatcher>>>,
829             ) -> Result<T, Box<dyn Error>> {
830                 return Ok(data);
831             }
832 
833             fn to_dbus(data: T) -> Result<T, Box<dyn Error>> {
834                 return Ok(data);
835             }
836         }
837 
838         impl<T: DBusArg> DBusArg for Vec<T> {
839             type DBusType = Vec<T::DBusType>;
840 
841             fn from_dbus(
842                 data: Vec<T::DBusType>,
843                 conn: Option<Arc<dbus::nonblock::SyncConnection>>,
844                 remote: Option<BusName<'static>>,
845                 disconnect_watcher: Option<Arc<Mutex<DisconnectWatcher>>>,
846             ) -> Result<Vec<T>, Box<dyn Error>> {
847                 let mut list: Vec<T> = vec![];
848                 for prop in data {
849                     let t = T::from_dbus(
850                         prop,
851                         conn.clone(),
852                         remote.clone(),
853                         disconnect_watcher.clone(),
854                     )?;
855                     list.push(t);
856                 }
857                 Ok(list)
858             }
859 
860             fn to_dbus(data: Vec<T>) -> Result<Vec<T::DBusType>, Box<dyn Error>> {
861                 let mut list: Vec<T::DBusType> = vec![];
862                 for item in data {
863                     let t = T::to_dbus(item)?;
864                     list.push(t);
865                 }
866                 Ok(list)
867             }
868         }
869     };
870 
871     debug_output_to_file(&gen, format!("out-generate_dbus_arg.rs"));
872 
873     gen.into()
874 }
875