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