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