1use proc_macro2::TokenStream;
2use quote::{format_ident, quote};
3use std::collections::BTreeMap;
4use syn::{
5 parse::{Parse, ParseStream},
6 parse_quote, parse_str,
7 punctuated::Punctuated,
8 spanned::Spanned,
9 token::{Async, Comma},
10 AngleBracketedGenericArguments, Attribute, Error, Expr, ExprLit, FnArg, GenericArgument, Ident,
11 ImplItem, ImplItemFn, ItemImpl,
12 Lit::Str,
13 Meta, MetaNameValue, PatType, PathArguments, ReturnType, Signature, Token, Type, TypePath,
14 Visibility,
15};
16use zvariant_utils::{case, def_attrs};
17
18use crate::utils::*;
19
20def_attrs! {
21 crate zbus;
22
23 pub ImplAttributes("impl block") {
24 interface str,
25 name str,
26 spawn bool,
27 introspection_docs bool,
28 proxy {
29 pub ProxyAttributes("proxy") {
32 assume_defaults bool,
33 default_path str,
34 default_service str,
35 async_name str,
36 blocking_name str,
37 gen_async bool,
38 gen_blocking bool,
39 visibility str
40 }
41 }
42 };
43
44 pub MethodAttributes("method") {
45 name str,
46 signal none,
47 property {
48 pub PropertyAttributes("property") {
49 emits_changed_signal str
50 }
51 },
52 out_args [str],
53 proxy {
54 pub ProxyMethodAttributes("proxy") {
57 object str,
58 async_object str,
59 blocking_object str,
60 no_reply none,
61 no_autostart none,
62 allow_interactive_auth none
63 }
64 }
65 };
66
67 pub ArgAttributes("argument") {
68 object_server none,
69 connection none,
70 header none,
71 signal_context none,
72 signal_emitter none
73 };
74}
75
76#[derive(Debug, Default)]
77struct Property {
78 read: bool,
79 write: bool,
80 emits_changed_signal: PropertyEmitsChangedSignal,
81 ty: Option<Type>,
82 doc_comments: TokenStream,
83}
84
85#[derive(Debug, PartialEq, Copy, Clone)]
86enum MethodType {
87 Signal,
88 Property(PropertyType),
89 Other,
90}
91
92#[derive(Debug, PartialEq, Copy, Clone)]
93enum PropertyType {
94 Setter,
95 Getter,
96}
97
98#[derive(Debug, Clone)]
99struct MethodInfo {
100 ident: Ident,
102 method_type: MethodType,
104 has_inputs: bool,
106 is_async: bool,
108 doc_comments: TokenStream,
110 is_mut: bool,
112 method_await: TokenStream,
114 typed_inputs: Vec<PatType>,
116 intro_args: TokenStream,
118 is_result_output: bool,
120 args_from_msg: TokenStream,
122 args_names: TokenStream,
124 reply: TokenStream,
126 signal_emitter_arg: Option<PatType>,
128 member_name: String,
130 proxy_attrs: Option<ProxyMethodAttributes>,
132 output: ReturnType,
134 cfg_attrs: Vec<Attribute>,
136 doc_attrs: Vec<Attribute>,
138}
139
140impl MethodInfo {
141 fn new(
142 zbus: &TokenStream,
143 method: &ImplItemFn,
144 attrs: &MethodAttributes,
145 cfg_attrs: &[&Attribute],
146 doc_attrs: &[&Attribute],
147 introspect_docs: bool,
148 ) -> syn::Result<MethodInfo> {
149 let is_async = method.sig.asyncness.is_some();
150 let Signature {
151 ident,
152 inputs,
153 output,
154 ..
155 } = &method.sig;
156 let doc_comments = if introspect_docs {
157 let docs = get_doc_attrs(&method.attrs)
158 .iter()
159 .filter_map(|attr| {
160 if let Ok(MetaNameValue {
161 value: Expr::Lit(ExprLit { lit: Str(s), .. }),
162 ..
163 }) = &attr.meta.require_name_value()
164 {
165 Some(s.value())
166 } else {
167 None
170 }
171 })
172 .collect();
173 to_xml_docs(docs)
174 } else {
175 quote!()
176 };
177 let is_property = attrs.property.is_some();
178 let is_signal = attrs.signal;
179 assert!(!is_property || !is_signal);
180
181 let mut typed_inputs = inputs
182 .iter()
183 .filter_map(typed_arg)
184 .cloned()
185 .collect::<Vec<_>>();
186
187 let has_inputs = count_regular_args(&typed_inputs) > 0;
188
189 let method_type = if is_signal {
190 MethodType::Signal
191 } else if is_property {
192 if has_inputs {
193 MethodType::Property(PropertyType::Setter)
194 } else {
195 MethodType::Property(PropertyType::Getter)
196 }
197 } else {
198 MethodType::Other
199 };
200
201 let is_mut = if let FnArg::Receiver(r) = inputs
202 .first()
203 .ok_or_else(|| Error::new_spanned(ident, "not &self method"))?
204 {
205 r.mutability.is_some()
206 } else if is_signal {
207 false
208 } else {
209 return Err(Error::new_spanned(method, "missing receiver"));
210 };
211 if is_signal && !is_async {
212 return Err(Error::new_spanned(method, "signals must be async"));
213 }
214 let method_await = if is_async {
215 quote! { .await }
216 } else {
217 quote! {}
218 };
219
220 let signal_emitter_arg: Option<PatType> = if is_signal {
221 if typed_inputs.is_empty() {
222 return Err(Error::new_spanned(
223 inputs,
224 "Expected a `&zbus::object_server::SignalEmitter<'_> argument",
225 ));
226 }
227 Some(typed_inputs.remove(0))
228 } else {
229 None
230 };
231
232 let mut intro_args = quote!();
233 intro_args.extend(introspect_input_args(&typed_inputs, is_signal, cfg_attrs));
234 let is_result_output = introspect_add_output_args(
235 &mut intro_args,
236 output,
237 attrs.out_args.as_deref(),
238 cfg_attrs,
239 )?;
240
241 let (args_from_msg, args_names) = get_args_from_inputs(&typed_inputs, method_type, zbus)?;
242
243 let reply = if is_result_output {
244 let ret = quote!(r);
245
246 quote!(match reply {
247 ::std::result::Result::Ok(r) => __zbus__connection.reply(&hdr, &#ret).await,
248 ::std::result::Result::Err(e) => __zbus__connection.reply_dbus_error(&hdr, e).await,
249 })
250 } else {
251 quote!(__zbus__connection.reply(&hdr, &reply).await)
252 };
253
254 let member_name = attrs.name.clone().unwrap_or_else(|| {
255 let mut name = ident.to_string();
256 if is_property && has_inputs {
257 assert!(name.starts_with("set_"));
258 name = name[4..].to_string();
259 }
260 pascal_case(&name)
261 });
262
263 Ok(MethodInfo {
264 ident: ident.clone(),
265 method_type,
266 has_inputs,
267 is_async,
268 doc_comments,
269 is_mut,
270 method_await,
271 typed_inputs,
272 signal_emitter_arg,
273 intro_args,
274 is_result_output,
275 args_from_msg,
276 args_names,
277 reply,
278 member_name,
279 proxy_attrs: attrs.proxy.clone(),
280 output: output.clone(),
281 cfg_attrs: cfg_attrs.iter().cloned().cloned().collect(),
282 doc_attrs: doc_attrs.iter().cloned().cloned().collect(),
283 })
284 }
285}
286
287pub fn expand(args: Punctuated<Meta, Token![,]>, mut input: ItemImpl) -> syn::Result<TokenStream> {
288 let zbus = zbus_path();
289
290 let self_ty = &input.self_ty;
291 let mut properties = BTreeMap::new();
292 let mut set_dispatch = quote!();
293 let mut set_mut_dispatch = quote!();
294 let mut get_dispatch = quote!();
295 let mut get_all = quote!();
296 let mut call_dispatch = quote!();
297 let mut call_mut_dispatch = quote!();
298 let mut introspect = quote!();
299 let mut generated_signals = quote!();
300 let mut signals_trait_methods = quote!();
301 let mut signals_emitter_impl_methods = quote!();
302 let mut signals_interface_ref_impl_methods = quote!();
303
304 let ty = match input.self_ty.as_ref() {
306 Type::Path(p) => {
307 &p.path
308 .segments
309 .last()
310 .ok_or_else(|| Error::new_spanned(p, "Unsupported 'impl' type"))?
311 .ident
312 }
313 _ => return Err(Error::new_spanned(&input.self_ty, "Invalid type")),
314 };
315
316 let impl_attrs = ImplAttributes::parse_nested_metas(args)?;
317 let iface_name = {
318 match (impl_attrs.name, impl_attrs.interface) {
319 (Some(name), None) | (None, Some(name)) => zbus_names::InterfaceName::try_from(name)
321 .map_err(|e| Error::new(input.span(), format!("{e}")))
322 .map(|i| i.to_string())?,
323 (None, None) => format!("org.freedesktop.{ty}"),
324 (Some(_), Some(_)) => {
325 return Err(syn::Error::new(
326 input.span(),
327 "`name` and `interface` attributes should not be specified at the same time",
328 ))
329 }
330 }
331 };
332 let with_spawn = impl_attrs.spawn.unwrap_or(true);
333 let mut proxy = impl_attrs
334 .proxy
335 .map(|p| Proxy::new(ty, &iface_name, p, &zbus));
336 let introspect_docs = impl_attrs.introspection_docs.unwrap_or(true);
337
338 let mut methods = vec![];
340 for item in &mut input.items {
341 let (method, is_signal) = match item {
342 ImplItem::Fn(m) => (m, false),
343 ImplItem::Verbatim(tokens) => {
345 let decl = syn::parse2::<ImplItemSignal>(tokens.clone())?;
347 let ImplItemSignal { attrs, vis, sig } = decl;
348 *item = ImplItem::Fn(ImplItemFn {
349 attrs,
350 vis,
351 defaultness: None,
352 sig,
353 block: parse_quote!({}),
355 });
356 match item {
357 ImplItem::Fn(m) => (m, true),
358 _ => unreachable!(),
359 }
360 }
361 _ => continue,
362 };
363
364 let method_attrs = MethodAttributes::parse(&method.attrs)?;
365
366 method.attrs.retain(|attr| !attr.path().is_ident("zbus"));
367
368 if is_signal && !method_attrs.signal {
369 return Err(syn::Error::new_spanned(
370 item,
371 "methods that are not signals must have a body",
372 ));
373 }
374
375 let cfg_attrs: Vec<_> = method
376 .attrs
377 .iter()
378 .filter(|a| a.path().is_ident("cfg"))
379 .collect();
380 let doc_attrs: Vec<_> = method
381 .attrs
382 .iter()
383 .filter(|a| a.path().is_ident("doc"))
384 .collect();
385
386 let method_info = MethodInfo::new(
387 &zbus,
388 method,
389 &method_attrs,
390 &cfg_attrs,
391 &doc_attrs,
392 introspect_docs,
393 )?;
394 let attr_property = method_attrs.property;
395 if let Some(prop_attrs) = &attr_property {
396 let property: &mut Property = properties
397 .entry(method_info.member_name.to_string())
398 .or_default();
399 if method_info.method_type == MethodType::Property(PropertyType::Getter) {
400 let emits_changed_signal = if let Some(s) = &prop_attrs.emits_changed_signal {
401 PropertyEmitsChangedSignal::parse(s, method.span())?
402 } else {
403 PropertyEmitsChangedSignal::True
404 };
405 property.read = true;
406 property.emits_changed_signal = emits_changed_signal;
407 } else {
408 property.write = true;
409 if prop_attrs.emits_changed_signal.is_some() {
410 return Err(Error::new_spanned(
411 method,
412 "`emits_changed_signal` cannot be specified on setters",
413 ));
414 }
415 }
416 }
417 methods.push((method, method_info));
418 }
419
420 for (method, method_info) in methods {
421 let info = method_info.clone();
422 let MethodInfo {
423 method_type,
424 has_inputs,
425 is_async,
426 doc_comments,
427 is_mut,
428 method_await,
429 typed_inputs,
430 signal_emitter_arg,
431 intro_args,
432 is_result_output,
433 args_from_msg,
434 args_names,
435 reply,
436 member_name,
437 cfg_attrs,
438 ..
439 } = method_info;
440
441 let mut method_clone = method.clone();
442 let Signature {
443 ident,
444 inputs,
445 output,
446 ..
447 } = &mut method.sig;
448
449 clear_input_arg_attrs(inputs);
450
451 match method_type {
452 MethodType::Signal => {
453 introspect.extend(doc_comments);
454 introspect.extend(introspect_signal(&member_name, &intro_args));
455 let signal_emitter = signal_emitter_arg.unwrap().pat;
456
457 method.block = parse_quote!({
458 #signal_emitter.emit(
459 <#self_ty as #zbus::object_server::Interface>::name(),
460 #member_name,
461 &(#args_names),
462 )
463 .await
464 });
465
466 method_clone.sig.asyncness = Some(Async(method_clone.span()));
467 *method_clone.sig.inputs.first_mut().unwrap() = parse_quote!(&self);
468 method_clone.vis = Visibility::Inherited;
469 let sig = &method_clone.sig;
470 signals_trait_methods.extend(quote! {
471 #sig;
472 });
473 method_clone.block = parse_quote!({
474 self.emit(
475 #iface_name,
476 #member_name,
477 &(#args_names),
478 )
479 .await
480 });
481 signals_emitter_impl_methods.extend(quote! {
482 #method_clone
483 });
484 method_clone.block = parse_quote!({
485 <#zbus::object_server::InterfaceRef<#self_ty>>::signal_emitter(self)
486 .emit(
487 #iface_name,
488 #member_name,
489 &(#args_names),
490 )
491 .await
492 });
493 signals_interface_ref_impl_methods.extend(quote! {
494 #method_clone
495 });
496 }
497 MethodType::Property(_) => {
498 let p = properties.get_mut(&member_name).unwrap();
499
500 let sk_member_name = case::snake_or_kebab_case(&member_name, true);
501 let prop_changed_method_name = format_ident!("{sk_member_name}_changed");
502 let prop_invalidate_method_name = format_ident!("{sk_member_name}_invalidate");
503
504 p.doc_comments.extend(doc_comments);
505 if has_inputs {
506 let set_call = if is_result_output {
507 quote!(self.#ident(#args_names)#method_await)
508 } else if is_async {
509 quote!(
510 ::std::result::Result::Ok(self.#ident(#args_names).await)
511 )
512 } else {
513 quote!(
514 ::std::result::Result::Ok(self.#ident(#args_names))
515 )
516 };
517
518 let value_to_owned = quote! {
527 match ::zbus::zvariant::Value::try_to_owned(value) {
528 ::std::result::Result::Ok(val) => ::zbus::zvariant::Value::from(val),
529 ::std::result::Result::Err(e) => {
530 return ::std::result::Result::Err(
531 ::std::convert::Into::into(#zbus::Error::Variant(::std::convert::Into::into(e)))
532 );
533 }
534 }
535 };
536
537 let value_param = typed_inputs
538 .iter()
539 .find(|input| {
540 let a = ArgAttributes::parse(&input.attrs).unwrap();
541 !a.object_server
542 && !a.connection
543 && !a.header
544 && !a.signal_context
545 && !a.signal_emitter
546 })
547 .ok_or_else(|| Error::new_spanned(inputs, "Expected a value argument"))?;
548
549 if !p.read {
552 p.ty = Some((*value_param.ty).clone());
553 p.emits_changed_signal = PropertyEmitsChangedSignal::False;
554 }
555
556 let value_arg = match &*value_param.ty {
557 Type::Reference(_) => quote!(value),
558 Type::Path(path) => path
559 .path
560 .segments
561 .first()
562 .map(|segment| match &segment.arguments {
563 PathArguments::AngleBracketed(angled) => angled
564 .args
565 .first()
566 .filter(|arg| matches!(arg, GenericArgument::Lifetime(_)))
567 .map(|_| quote!(match ::zbus::zvariant::Value::try_clone(value) {
568 ::std::result::Result::Ok(val) => val,
569 ::std::result::Result::Err(e) => {
570 return ::std::result::Result::Err(
571 ::std::convert::Into::into(#zbus::Error::Variant(::std::convert::Into::into(e)))
572 );
573 }
574 }))
575 .unwrap_or_else(|| value_to_owned.clone()),
576 _ => value_to_owned.clone(),
577 })
578 .unwrap_or_else(|| value_to_owned.clone()),
579 _ => value_to_owned,
580 };
581
582 let value_param_name = &value_param.pat;
583 let prop_changed_method = match p.emits_changed_signal {
584 PropertyEmitsChangedSignal::True => {
585 quote!({
586 self
587 .#prop_changed_method_name(&__zbus__signal_emitter)
588 .await
589 .map(|_| set_result)
590 .map_err(Into::into)
591 })
592 }
593 PropertyEmitsChangedSignal::Invalidates => {
594 quote!({
595 self
596 .#prop_invalidate_method_name(&__zbus__signal_emitter)
597 .await
598 .map(|_| set_result)
599 .map_err(Into::into)
600 })
601 }
602 PropertyEmitsChangedSignal::False | PropertyEmitsChangedSignal::Const => {
603 quote!({ Ok(()) })
604 }
605 };
606 let do_set = quote!({
607 #args_from_msg
608 let value = #value_arg;
609 match ::std::convert::TryInto::try_into(value) {
610 ::std::result::Result::Ok(val) => {
611 let #value_param_name = val;
612 match #set_call {
613 ::std::result::Result::Ok(set_result) => #prop_changed_method
614 e => e,
615 }
616 }
617 ::std::result::Result::Err(e) => {
618 ::std::result::Result::Err(
619 ::std::convert::Into::into(#zbus::Error::Variant(::std::convert::Into::into(e))),
620 )
621 }
622 }
623 });
624
625 if is_mut {
626 let q = quote!(
627 #(#cfg_attrs)*
628 #member_name => {
629 ::std::option::Option::Some((move || async move { #do_set }) ().await)
630 }
631 );
632 set_mut_dispatch.extend(q);
633
634 let q = quote!(
635 #(#cfg_attrs)*
636 #member_name => #zbus::object_server::DispatchResult::RequiresMut,
637 );
638 set_dispatch.extend(q);
639 } else {
640 let q = quote!(
641 #(#cfg_attrs)*
642 #member_name => {
643 #zbus::object_server::DispatchResult::Async(::std::boxed::Box::pin(async move {
644 #do_set
645 }))
646 }
647 );
648 set_dispatch.extend(q);
649 }
650 } else {
651 let is_fallible_property = is_result_output;
652
653 p.ty = Some(get_return_type(output)?.clone());
654
655 let value_convert = quote!(
656 <#zbus::zvariant::OwnedValue as ::std::convert::TryFrom<_>>::try_from(
657 <#zbus::zvariant::Value as ::std::convert::From<_>>::from(
658 value,
659 ),
660 )
661 .map_err(|e| #zbus::fdo::Error::Failed(e.to_string()))
662 );
663 let inner = if is_fallible_property {
664 quote!(self.#ident(#args_names) #method_await .and_then(|value| #value_convert))
665 } else {
666 quote!({
667 let value = self.#ident(#args_names)#method_await;
668 #value_convert
669 })
670 };
671
672 let q = quote!(
673 #(#cfg_attrs)*
674 #member_name => {
675 #args_from_msg
676 ::std::option::Option::Some(#inner)
677 },
678 );
679 get_dispatch.extend(q);
680
681 let q = if is_fallible_property {
682 quote!({
683 #args_from_msg
684 if let Ok(prop) = self.#ident(#args_names)#method_await {
685 props.insert(
686 ::std::string::ToString::to_string(#member_name),
687 <#zbus::zvariant::OwnedValue as ::std::convert::TryFrom<_>>::try_from(
688 <#zbus::zvariant::Value as ::std::convert::From<_>>::from(
689 prop,
690 ),
691 )
692 .map_err(|e| #zbus::fdo::Error::Failed(e.to_string()))?,
693 );
694 }})
695 } else {
696 quote!({
697 #args_from_msg
698 props.insert(
699 ::std::string::ToString::to_string(#member_name),
700 <#zbus::zvariant::OwnedValue as ::std::convert::TryFrom<_>>::try_from(
701 <#zbus::zvariant::Value as ::std::convert::From<_>>::from(
702 self.#ident(#args_names)#method_await,
703 ),
704 )
705 .map_err(|e| #zbus::fdo::Error::Failed(e.to_string()))?,
706 );
707 })
708 };
709
710 get_all.extend(q);
711
712 let prop_value_handled = if is_fallible_property {
713 quote!(self.#ident(#args_names)#method_await?)
714 } else {
715 quote!(self.#ident(#args_names)#method_await)
716 };
717
718 if p.emits_changed_signal == PropertyEmitsChangedSignal::True {
719 let changed_doc = format!(
720 "Emit the “PropertiesChanged” signal with the new value for the\n\
721 `{member_name}` property.\n\n\
722 This method should be called if a property value changes outside\n\
723 its setter method."
724 );
725 let prop_changed_method = quote!(
726 #[doc = #changed_doc]
727 pub async fn #prop_changed_method_name(
728 &self,
729 __zbus__signal_emitter: &#zbus::object_server::SignalEmitter<'_>,
730 ) -> #zbus::Result<()> {
731 let __zbus__header = ::std::option::Option::None::<&#zbus::message::Header<'_>>;
732 let __zbus__connection = __zbus__signal_emitter.connection();
733 let __zbus__object_server = __zbus__connection.object_server();
734 #args_from_msg
735 let mut changed = ::std::collections::HashMap::new();
736 let value = <#zbus::zvariant::Value as ::std::convert::From<_>>::from(#prop_value_handled);
737 changed.insert(#member_name, value);
738 #zbus::fdo::Properties::properties_changed(
739 __zbus__signal_emitter,
740 #zbus::names::InterfaceName::from_static_str_unchecked(#iface_name),
741 changed,
742 ::std::borrow::Cow::Borrowed(&[]),
743 ).await
744 }
745 );
746
747 generated_signals.extend(prop_changed_method);
748 }
749
750 if p.emits_changed_signal == PropertyEmitsChangedSignal::Invalidates {
751 let invalidate_doc = format!(
752 "Emit the “PropertiesChanged” signal for the `{member_name}` property\n\
753 without including the new value.\n\n\
754 It is usually better to call `{prop_changed_method_name}` instead so\n\
755 that interested peers do not need to fetch the new value separately\n\
756 (causing excess traffic on the bus)."
757 );
758 let prop_invalidate_method = quote!(
759 #[doc = #invalidate_doc]
760 pub async fn #prop_invalidate_method_name(
761 &self,
762 __zbus__signal_emitter: &#zbus::object_server::SignalEmitter<'_>,
763 ) -> #zbus::Result<()> {
764 #zbus::fdo::Properties::properties_changed(
765 __zbus__signal_emitter,
766 #zbus::names::InterfaceName::from_static_str_unchecked(#iface_name),
767 ::std::collections::HashMap::new(),
768 ::std::borrow::Cow::Borrowed(&[#member_name]),
769 ).await
770 }
771 );
772
773 generated_signals.extend(prop_invalidate_method);
774 }
775 }
776 }
777 MethodType::Other => {
778 introspect.extend(doc_comments);
779 introspect.extend(introspect_method(&member_name, &intro_args));
780
781 let m = quote! {
782 #(#cfg_attrs)*
783 #member_name => {
784 let future = async move {
785 #args_from_msg
786 let reply = self.#ident(#args_names)#method_await;
787 let hdr = __zbus__message.header();
788 if hdr.primary().flags().contains(zbus::message::Flags::NoReplyExpected) {
789 Ok(())
790 } else {
791 #reply
792 }
793 };
794 #zbus::object_server::DispatchResult::Async(::std::boxed::Box::pin(async move {
795 future.await
796 }))
797 },
798 };
799
800 if is_mut {
801 call_dispatch.extend(quote! {
802 #(#cfg_attrs)*
803 #member_name => #zbus::object_server::DispatchResult::RequiresMut,
804 });
805 call_mut_dispatch.extend(m);
806 } else {
807 call_dispatch.extend(m);
808 }
809 }
810 }
811
812 if let Some(proxy) = &mut proxy {
813 proxy.add_method(info, &properties)?;
814 }
815 }
816
817 introspect_properties(&mut introspect, properties)?;
818
819 let generics = &input.generics;
820 let where_clause = &generics.where_clause;
821
822 let generated_signals_impl = if generated_signals.is_empty() {
823 quote!()
824 } else {
825 quote! {
826 impl #generics #self_ty
827 #where_clause
828 {
829 #generated_signals
830 }
831 }
832 };
833 let signals_trait_and_impl = if signals_trait_methods.is_empty() {
834 quote!()
835 } else {
836 let signals_trait_name = format_ident!("{}Signals", ty);
837 let signals_trait_doc = format!("Trait providing all signal emission methods for `{ty}`.");
838
839 quote! {
840 #[doc = #signals_trait_doc]
841 #[#zbus::export::async_trait::async_trait]
842 pub trait #signals_trait_name {
843 #signals_trait_methods
844 }
845
846 #[#zbus::export::async_trait::async_trait]
847 impl #signals_trait_name for #zbus::object_server::SignalEmitter<'_>
848 {
849 #signals_emitter_impl_methods
850 }
851
852 #[#zbus::export::async_trait::async_trait]
853 impl #generics #signals_trait_name for #zbus::object_server::InterfaceRef<#self_ty>
854 #where_clause
855 {
856 #signals_interface_ref_impl_methods
857 }
858 }
859 };
860
861 let proxy = proxy.map(|proxy| proxy.gen()).transpose()?;
862 let introspect_format_str = format!("{}<interface name=\"{iface_name}\">", "{:indent$}");
863
864 Ok(quote! {
865 #input
866
867 #generated_signals_impl
868
869 #signals_trait_and_impl
870
871 #[#zbus::export::async_trait::async_trait]
872 impl #generics #zbus::object_server::Interface for #self_ty
873 #where_clause
874 {
875 fn name() -> #zbus::names::InterfaceName<'static> {
876 #zbus::names::InterfaceName::from_static_str_unchecked(#iface_name)
877 }
878
879 fn spawn_tasks_for_methods(&self) -> bool {
880 #with_spawn
881 }
882
883 async fn get(
884 &self,
885 __zbus__property_name: &str,
886 __zbus__object_server: &#zbus::ObjectServer,
887 __zbus__connection: &#zbus::Connection,
888 __zbus__header: Option<&#zbus::message::Header<'_>>,
889 __zbus__signal_emitter: &#zbus::object_server::SignalEmitter<'_>,
890 ) -> ::std::option::Option<#zbus::fdo::Result<#zbus::zvariant::OwnedValue>> {
891 match __zbus__property_name {
892 #get_dispatch
893 _ => ::std::option::Option::None,
894 }
895 }
896
897 async fn get_all(
898 &self,
899 __zbus__object_server: &#zbus::ObjectServer,
900 __zbus__connection: &#zbus::Connection,
901 __zbus__header: Option<&#zbus::message::Header<'_>>,
902 __zbus__signal_emitter: &#zbus::object_server::SignalEmitter<'_>,
903 ) -> #zbus::fdo::Result<::std::collections::HashMap<
904 ::std::string::String,
905 #zbus::zvariant::OwnedValue,
906 >> {
907 let mut props: ::std::collections::HashMap<
908 ::std::string::String,
909 #zbus::zvariant::OwnedValue,
910 > = ::std::collections::HashMap::new();
911 #get_all
912 Ok(props)
913 }
914
915 fn set<'call>(
916 &'call self,
917 __zbus__property_name: &'call str,
918 value: &'call #zbus::zvariant::Value<'_>,
919 __zbus__object_server: &'call #zbus::ObjectServer,
920 __zbus__connection: &'call #zbus::Connection,
921 __zbus__header: Option<&'call #zbus::message::Header<'_>>,
922 __zbus__signal_emitter: &'call #zbus::object_server::SignalEmitter<'_>,
923 ) -> #zbus::object_server::DispatchResult<'call> {
924 match __zbus__property_name {
925 #set_dispatch
926 _ => #zbus::object_server::DispatchResult::NotFound,
927 }
928 }
929
930 async fn set_mut(
931 &mut self,
932 __zbus__property_name: &str,
933 value: &#zbus::zvariant::Value<'_>,
934 __zbus__object_server: &#zbus::ObjectServer,
935 __zbus__connection: &#zbus::Connection,
936 __zbus__header: Option<&#zbus::message::Header<'_>>,
937 __zbus__signal_emitter: &#zbus::object_server::SignalEmitter<'_>,
938 ) -> ::std::option::Option<#zbus::fdo::Result<()>> {
939 match __zbus__property_name {
940 #set_mut_dispatch
941 _ => ::std::option::Option::None,
942 }
943 }
944
945 fn call<'call>(
946 &'call self,
947 __zbus__object_server: &'call #zbus::ObjectServer,
948 __zbus__connection: &'call #zbus::Connection,
949 __zbus__message: &'call #zbus::message::Message,
950 name: #zbus::names::MemberName<'call>,
951 ) -> #zbus::object_server::DispatchResult<'call> {
952 match name.as_str() {
953 #call_dispatch
954 _ => #zbus::object_server::DispatchResult::NotFound,
955 }
956 }
957
958 fn call_mut<'call>(
959 &'call mut self,
960 __zbus__object_server: &'call #zbus::ObjectServer,
961 __zbus__connection: &'call #zbus::Connection,
962 __zbus__message: &'call #zbus::message::Message,
963 name: #zbus::names::MemberName<'call>,
964 ) -> #zbus::object_server::DispatchResult<'call> {
965 match name.as_str() {
966 #call_mut_dispatch
967 _ => #zbus::object_server::DispatchResult::NotFound,
968 }
969 }
970
971 fn introspect_to_writer(&self, writer: &mut dyn ::std::fmt::Write, level: usize) {
972 ::std::writeln!(
973 writer,
974 #introspect_format_str,
975 "",
976 indent = level
977 ).unwrap();
978 {
979 use #zbus::zvariant::Type;
980
981 let level = level + 2;
982 #introspect
983 }
984 ::std::writeln!(writer, r#"{:indent$}</interface>"#, "", indent = level).unwrap();
985 }
986 }
987
988 #proxy
989 })
990}
991
992fn get_args_from_inputs(
993 inputs: &[PatType],
994 method_type: MethodType,
995 zbus: &TokenStream,
996) -> syn::Result<(TokenStream, TokenStream)> {
997 if inputs.is_empty() {
998 Ok((quote!(), quote!()))
999 } else {
1000 let mut server_arg_decl = None;
1001 let mut conn_arg_decl = None;
1002 let mut header_arg_decl = None;
1003 let mut signal_emitter_arg_decl = None;
1004 let mut args_names = Vec::new();
1005 let mut tys = Vec::new();
1006
1007 for input in inputs {
1008 let ArgAttributes {
1009 object_server,
1010 connection,
1011 header,
1012 signal_emitter,
1013 signal_context,
1014 } = ArgAttributes::parse(&input.attrs)?;
1015
1016 if object_server {
1017 if server_arg_decl.is_some() {
1018 return Err(Error::new_spanned(
1019 input,
1020 "There can only be one object_server argument",
1021 ));
1022 }
1023
1024 let server_arg = &input.pat;
1025 server_arg_decl = Some(quote! { let #server_arg = &__zbus__object_server; });
1026 } else if connection {
1027 if conn_arg_decl.is_some() {
1028 return Err(Error::new_spanned(
1029 input,
1030 "There can only be one connection argument",
1031 ));
1032 }
1033
1034 let conn_arg = &input.pat;
1035 conn_arg_decl = Some(quote! { let #conn_arg = &__zbus__connection; });
1036 } else if header {
1037 if header_arg_decl.is_some() {
1038 return Err(Error::new_spanned(
1039 input,
1040 "There can only be one header argument",
1041 ));
1042 }
1043
1044 let header_arg = &input.pat;
1045
1046 header_arg_decl = match method_type {
1047 MethodType::Property(_) => Some(quote! {
1048 let #header_arg =
1049 ::std::option::Option::<&#zbus::message::Header<'_>>::cloned(
1050 __zbus__header,
1051 );
1052 }),
1053 _ => Some(quote! { let #header_arg = __zbus__message.header(); }),
1054 };
1055 } else if signal_context || signal_emitter {
1056 if signal_emitter_arg_decl.is_some() {
1057 return Err(Error::new_spanned(
1058 input,
1059 "There can only be one `signal_emitter` or `signal_context` argument",
1060 ));
1061 }
1062
1063 let signal_context_arg = &input.pat;
1064
1065 signal_emitter_arg_decl = match method_type {
1066 MethodType::Property(_) => Some(
1067 quote! { let #signal_context_arg = ::std::clone::Clone::clone(__zbus__signal_emitter); },
1068 ),
1069 _ => Some(quote! {
1070 let #signal_context_arg = match hdr.path() {
1071 ::std::option::Option::Some(p) => {
1072 #zbus::object_server::SignalEmitter::new(__zbus__connection, p).expect("Infallible conversion failed")
1073 }
1074 ::std::option::Option::None => {
1075 let err = #zbus::fdo::Error::UnknownObject("Path Required".into());
1076 return __zbus__connection.reply_dbus_error(&hdr, err).await;
1077 }
1078 };
1079 }),
1080 };
1081 } else {
1082 args_names.push(pat_ident(input).unwrap());
1083 tys.push(&input.ty);
1084 }
1085 }
1086
1087 let (hdr_init, msg_init, args_decl) = match method_type {
1088 MethodType::Property(PropertyType::Getter) => (quote! {}, quote! {}, quote! {}),
1089 MethodType::Property(PropertyType::Setter) => (
1090 quote! { let hdr = __zbus__header.as_ref().unwrap(); },
1091 quote! {},
1092 quote! {},
1093 ),
1094 _ => (
1095 quote! { let hdr = __zbus__message.header(); },
1096 quote! { let msg_body = __zbus__message.body(); },
1097 quote! {
1098 let (#(#args_names),*): (#(#tys),*) =
1099 match msg_body.deserialize() {
1100 ::std::result::Result::Ok(r) => r,
1101 ::std::result::Result::Err(e) => {
1102 let err = <#zbus::fdo::Error as ::std::convert::From<_>>::from(e);
1103 return __zbus__connection.reply_dbus_error(&hdr, err).await;
1104 }
1105 };
1106 },
1107 ),
1108 };
1109
1110 let args_from_msg = quote! {
1111 #hdr_init
1112
1113 #msg_init
1114
1115 #server_arg_decl
1116
1117 #conn_arg_decl
1118
1119 #header_arg_decl
1120
1121 #signal_emitter_arg_decl
1122
1123 #args_decl
1124 };
1125
1126 let all_args_names = inputs.iter().filter_map(pat_ident);
1127 let all_args_names = quote! { #(#all_args_names,)* };
1128
1129 Ok((args_from_msg, all_args_names))
1130 }
1131}
1132
1133fn clear_input_arg_attrs(inputs: &mut Punctuated<FnArg, Token![,]>) {
1135 for input in inputs {
1136 if let FnArg::Typed(t) = input {
1137 t.attrs.retain(|attr| !attr.path().is_ident("zbus"));
1138 }
1139 }
1140}
1141
1142fn introspect_signal(name: &str, args: &TokenStream) -> TokenStream {
1143 let format_str = format!("{}<signal name=\"{name}\">", "{:indent$}");
1144 quote!(
1145 ::std::writeln!(writer, #format_str, "", indent = level).unwrap();
1146 {
1147 let level = level + 2;
1148 #args
1149 }
1150 ::std::writeln!(writer, "{:indent$}</signal>", "", indent = level).unwrap();
1151 )
1152}
1153
1154fn introspect_method(name: &str, args: &TokenStream) -> TokenStream {
1155 let format_str = format!("{}<method name=\"{name}\">", "{:indent$}");
1156 quote!(
1157 ::std::writeln!(writer, #format_str, "", indent = level).unwrap();
1158 {
1159 let level = level + 2;
1160 #args
1161 }
1162 ::std::writeln!(writer, "{:indent$}</method>", "", indent = level).unwrap();
1163 )
1164}
1165
1166fn introspect_input_args<'i>(
1167 inputs: &'i [PatType],
1168 is_signal: bool,
1169 cfg_attrs: &'i [&'i syn::Attribute],
1170) -> impl Iterator<Item = TokenStream> + 'i {
1171 inputs
1172 .iter()
1173 .filter_map(move |pat_type @ PatType { ty, attrs, .. }| {
1174 if is_special_arg(attrs) {
1175 return None;
1176 }
1177
1178 let ident = pat_ident(pat_type).unwrap();
1179 let arg_name = quote!(#ident).to_string();
1180 let dir = if is_signal { "" } else { " direction=\"in\"" };
1181 let format_str = format!(
1182 "{}<arg name=\"{arg_name}\" type=\"{}\"{dir}/>",
1183 "{:indent$}", "{}",
1184 );
1185 Some(quote!(
1186 #(#cfg_attrs)*
1187 ::std::writeln!(writer, #format_str, "", <#ty>::SIGNATURE, indent = level).unwrap();
1188 ))
1189 })
1190}
1191
1192fn count_regular_args(inputs: &[PatType]) -> usize {
1193 inputs
1194 .iter()
1195 .filter(|PatType { attrs, .. }| !is_special_arg(attrs))
1196 .count()
1197}
1198
1199fn is_special_arg(attrs: &[Attribute]) -> bool {
1200 attrs.iter().any(|attr| {
1201 if !attr.path().is_ident("zbus") {
1202 return false;
1203 }
1204
1205 let Ok(list) = &attr.meta.require_list() else {
1206 return false;
1207 };
1208 let Ok(nested) = list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
1209 else {
1210 return false;
1211 };
1212
1213 let res = nested.iter().any(|nested_meta| {
1214 matches!(
1215 nested_meta,
1216 Meta::Path(path)
1217 if path.is_ident("object_server") ||
1218 path.is_ident("connection") ||
1219 path.is_ident("header") ||
1220 path.is_ident("signal_context") ||
1221 path.is_ident("signal_emitter")
1222 )
1223 });
1224
1225 res
1226 })
1227}
1228
1229fn introspect_output_arg(
1230 ty: &Type,
1231 arg_name: Option<&String>,
1232 cfg_attrs: &[&syn::Attribute],
1233) -> TokenStream {
1234 let arg_name_attr = match arg_name {
1235 Some(name) => format!("name=\"{name}\" "),
1236 None => String::from(""),
1237 };
1238
1239 let format_str = format!(
1240 "{}<arg {arg_name_attr}type=\"{}\" direction=\"out\"/>",
1241 "{:indent$}", "{}",
1242 );
1243 quote!(
1244 #(#cfg_attrs)*
1245 ::std::writeln!(writer, #format_str, "", <#ty>::SIGNATURE, indent = level).unwrap();
1246 )
1247}
1248
1249fn get_result_inner_type(p: &TypePath) -> syn::Result<&Type> {
1250 if let PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }) = &p
1251 .path
1252 .segments
1253 .last()
1254 .ok_or_else(|| Error::new_spanned(p, "unsupported result type"))?
1255 .arguments
1256 {
1257 if let Some(syn::GenericArgument::Type(ty)) = args.first() {
1258 return Ok(ty);
1259 }
1260 }
1261
1262 Err(Error::new_spanned(p, "unhandled Result return"))
1263}
1264
1265fn introspect_add_output_args(
1266 args: &mut TokenStream,
1267 output: &ReturnType,
1268 arg_names: Option<&[String]>,
1269 cfg_attrs: &[&syn::Attribute],
1270) -> syn::Result<bool> {
1271 let mut is_result_output = false;
1272
1273 if let ReturnType::Type(_, ty) = output {
1274 let mut ty = ty.as_ref();
1275
1276 if let Type::Path(p) = ty {
1277 is_result_output = p
1278 .path
1279 .segments
1280 .last()
1281 .ok_or_else(|| Error::new_spanned(ty, "unsupported output type"))?
1282 .ident
1283 == "Result";
1284 if is_result_output {
1285 ty = get_result_inner_type(p)?;
1286 }
1287 }
1288
1289 if let Type::Tuple(t) = ty {
1290 if let Some(arg_names) = arg_names {
1291 if t.elems.len() != arg_names.len() {
1292 panic!("Number of out arg names different from out args specified")
1294 }
1295 }
1296 for i in 0..t.elems.len() {
1297 let name = arg_names.map(|names| &names[i]);
1298 args.extend(introspect_output_arg(&t.elems[i], name, cfg_attrs));
1299 }
1300 } else {
1301 args.extend(introspect_output_arg(ty, None, cfg_attrs));
1302 }
1303 }
1304
1305 Ok(is_result_output)
1306}
1307
1308fn get_return_type(output: &ReturnType) -> syn::Result<&Type> {
1309 if let ReturnType::Type(_, ty) = output {
1310 let ty = ty.as_ref();
1311
1312 if let Type::Path(p) = ty {
1313 let is_result_output = p
1314 .path
1315 .segments
1316 .last()
1317 .ok_or_else(|| Error::new_spanned(ty, "unsupported property type"))?
1318 .ident
1319 == "Result";
1320 if is_result_output {
1321 return get_result_inner_type(p);
1322 }
1323 }
1324
1325 Ok(ty)
1326 } else {
1327 Err(Error::new_spanned(output, "Invalid return type"))
1328 }
1329}
1330
1331fn introspect_properties(
1332 introspection: &mut TokenStream,
1333 properties: BTreeMap<String, Property>,
1334) -> syn::Result<()> {
1335 for (name, prop) in properties {
1336 let access = if prop.read && prop.write {
1337 "readwrite"
1338 } else if prop.read {
1339 "read"
1340 } else if prop.write {
1341 "write"
1342 } else {
1343 unreachable!("Properties should have at least one access type");
1344 };
1345 let ty = prop.ty.unwrap();
1346
1347 let doc_comments = prop.doc_comments;
1348 if prop.emits_changed_signal == PropertyEmitsChangedSignal::True {
1349 let format_str = format!(
1350 "{}<property name=\"{name}\" type=\"{}\" access=\"{access}\"/>",
1351 "{:indent$}", "{}",
1352 );
1353 introspection.extend(quote!(
1354 #doc_comments
1355 ::std::writeln!(writer, #format_str, "", <#ty>::SIGNATURE, indent = level).unwrap();
1356 ));
1357 } else {
1358 let emits_changed_signal = prop.emits_changed_signal.to_string();
1359 let annot_name = "org.freedesktop.DBus.Property.EmitsChangedSignal";
1360 let format_str = format!(
1361 "{}<property name=\"{name}\" type=\"{}\" access=\"{access}\">\n\
1362 {}<annotation name=\"{annot_name}\" value=\"{emits_changed_signal}\"/>\n\
1363 {}</property>",
1364 "{:indent$}", "{}", "{:annot_indent$}", "{:indent$}",
1365 );
1366 introspection.extend(quote!(
1367 #doc_comments
1368 ::std::writeln!(
1369 writer,
1370 #format_str,
1371 "", <#ty>::SIGNATURE, "", "", indent = level, annot_indent = level + 2,
1372 ).unwrap();
1373 ));
1374 }
1375 }
1376
1377 Ok(())
1378}
1379
1380pub fn to_xml_docs(lines: Vec<String>) -> TokenStream {
1381 let mut docs = quote!();
1382
1383 let mut lines: Vec<&str> = lines
1384 .iter()
1385 .skip_while(|s| is_blank(s))
1386 .flat_map(|s| s.split('\n'))
1387 .collect();
1388
1389 while let Some(true) = lines.last().map(|s| is_blank(s)) {
1390 lines.pop();
1391 }
1392
1393 if lines.is_empty() {
1394 return docs;
1395 }
1396
1397 docs.extend(quote!(::std::writeln!(writer, "{:indent$}<!--", "", indent = level).unwrap();));
1398 for line in lines {
1399 if !line.is_empty() {
1400 docs.extend(
1401 quote!(::std::writeln!(writer, "{:indent$}{}", "", #line, indent = level).unwrap();),
1402 );
1403 } else {
1404 docs.extend(quote!(::std::writeln!(writer, "").unwrap();));
1405 }
1406 }
1407 docs.extend(quote!(::std::writeln!(writer, "{:indent$} -->", "", indent = level).unwrap();));
1408
1409 docs
1410}
1411
1412struct ImplItemSignal {
1414 attrs: Vec<Attribute>,
1415 vis: Visibility,
1416 sig: Signature,
1417}
1418
1419impl Parse for ImplItemSignal {
1420 fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
1421 let attrs = input.call(Attribute::parse_outer)?;
1422 let vis = input.parse()?;
1423 let sig = input.parse()?;
1424 let _: Token![;] = input.parse()?;
1425
1426 Ok(ImplItemSignal { attrs, vis, sig })
1427 }
1428}
1429
1430#[derive(Debug)]
1431struct Proxy {
1432 ty: Ident,
1434 iface_name: String,
1436 zbus: TokenStream,
1438
1439 attrs: ProxyAttributes,
1441
1442 methods: TokenStream,
1444}
1445
1446impl Proxy {
1447 fn new(ty: &Ident, iface_name: &str, attrs: ProxyAttributes, zbus: &TokenStream) -> Self {
1448 Self {
1449 iface_name: iface_name.to_string(),
1450 ty: ty.clone(),
1451 zbus: zbus.clone(),
1452 attrs,
1453 methods: quote!(),
1454 }
1455 }
1456
1457 fn add_method(
1458 &mut self,
1459 method_info: MethodInfo,
1460 properties: &BTreeMap<String, Property>,
1461 ) -> syn::Result<()> {
1462 let inputs: Punctuated<PatType, Comma> = method_info
1463 .typed_inputs
1464 .iter()
1465 .filter(|input| {
1466 let a = ArgAttributes::parse(&input.attrs).unwrap();
1467 !a.object_server
1468 && !a.connection
1469 && !a.header
1470 && !a.signal_context
1471 && !a.signal_emitter
1472 })
1473 .cloned()
1474 .collect();
1475 let zbus = &self.zbus;
1476 let ret = match &method_info.output {
1477 ReturnType::Type(_, ty) => {
1478 let ty = ty.as_ref();
1479
1480 if let Type::Path(p) = ty {
1481 let is_result_output = p
1482 .path
1483 .segments
1484 .last()
1485 .ok_or_else(|| Error::new_spanned(ty, "unsupported return type"))?
1486 .ident
1487 == "Result";
1488 if is_result_output {
1489 let is_prop = matches!(method_info.method_type, MethodType::Property(_));
1490
1491 if is_prop {
1492 let inner_ty = get_result_inner_type(p)?;
1494 quote! { #zbus::Result<#inner_ty> }
1495 } else {
1496 quote! { #ty }
1497 }
1498 } else {
1499 quote! { #zbus::Result<#ty> }
1500 }
1501 } else {
1502 quote! { #zbus::Result<#ty> }
1503 }
1504 }
1505 ReturnType::Default => quote! { #zbus::Result<()> },
1506 };
1507 let ident = &method_info.ident;
1508 let member_name = method_info.member_name;
1509 let mut proxy_method_attrs = quote! { name = #member_name, };
1510 proxy_method_attrs.extend(match method_info.method_type {
1511 MethodType::Signal => quote!(signal),
1512 MethodType::Property(_) => {
1513 let emits_changed_signal = properties
1514 .get(&member_name)
1515 .unwrap()
1516 .emits_changed_signal
1517 .to_string();
1518 let emits_changed_signal = quote! { emits_changed_signal = #emits_changed_signal };
1519
1520 quote! { property(#emits_changed_signal) }
1521 }
1522 MethodType::Other => quote!(),
1523 });
1524 if let Some(attrs) = method_info.proxy_attrs {
1525 if let Some(object) = attrs.object {
1526 proxy_method_attrs.extend(quote! { object = #object, });
1527 }
1528 if let Some(async_object) = attrs.async_object {
1529 proxy_method_attrs.extend(quote! { async_object = #async_object, });
1530 }
1531 if let Some(blocking_object) = attrs.blocking_object {
1532 proxy_method_attrs.extend(quote! { blocking_object = #blocking_object, });
1533 }
1534 if attrs.no_reply {
1535 proxy_method_attrs.extend(quote! { no_reply, });
1536 }
1537 if attrs.no_autostart {
1538 proxy_method_attrs.extend(quote! { no_autostart, });
1539 }
1540 if attrs.allow_interactive_auth {
1541 proxy_method_attrs.extend(quote! { allow_interactive_auth, });
1542 }
1543 }
1544 let cfg_attrs = method_info.cfg_attrs;
1545 let doc_attrs = method_info.doc_attrs;
1546 self.methods.extend(quote! {
1547 #(#cfg_attrs)*
1548 #(#doc_attrs)*
1549 #[zbus(#proxy_method_attrs)]
1550 fn #ident(&self, #inputs) -> #ret;
1551 });
1552
1553 Ok(())
1554 }
1555
1556 fn gen(&self) -> syn::Result<TokenStream> {
1557 let attrs = &self.attrs;
1558 let (
1559 assume_defaults,
1560 default_path,
1561 default_service,
1562 async_name,
1563 blocking_name,
1564 gen_async,
1565 gen_blocking,
1566 ty,
1567 methods,
1568 ) = (
1569 attrs
1570 .assume_defaults
1571 .map(|value| quote! { assume_defaults = #value, }),
1572 attrs
1573 .default_path
1574 .as_ref()
1575 .map(|value| quote! { default_path = #value, }),
1576 attrs
1577 .default_service
1578 .as_ref()
1579 .map(|value| quote! { default_service = #value, }),
1580 attrs
1581 .async_name
1582 .as_ref()
1583 .map(|value| quote! { async_name = #value, }),
1584 attrs
1585 .blocking_name
1586 .as_ref()
1587 .map(|value| quote! { blocking_name = #value, }),
1588 attrs.gen_async.map(|value| quote! { gen_async = #value, }),
1589 attrs
1590 .gen_blocking
1591 .map(|value| quote! { gen_blocking = #value, }),
1592 &self.ty,
1593 &self.methods,
1594 );
1595 let iface_name = &self.iface_name;
1596 let vis = match &self.attrs.visibility {
1597 Some(s) => parse_str::<Visibility>(s)?,
1598 None => Visibility::Public(Token)),
1599 };
1600 let zbus = &self.zbus;
1601 let proxy_doc = format!("Proxy for the `{iface_name}` interface.");
1602 Ok(quote! {
1603 #[doc = #proxy_doc]
1604 #[#zbus::proxy(
1605 name = #iface_name,
1606 #assume_defaults
1607 #default_path
1608 #default_service
1609 #async_name
1610 #blocking_name
1611 #gen_async
1612 #gen_blocking
1613 )]
1614 #vis trait #ty {
1615 #methods
1616 }
1617 })
1618 }
1619}