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