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