zbus_macros/
iface.rs

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            // Keep this in sync with proxy's method attributes.
30            // TODO: Find a way to share code with proxy module.
31            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            // Keep this in sync with proxy's method attributes.
55            // TODO: Find a way to share code with proxy module.
56            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    /// The method identifier
101    ident: Ident,
102    /// The type of method being parsed
103    method_type: MethodType,
104    /// Whether the method has inputs
105    has_inputs: bool,
106    /// Whether the method is async
107    is_async: bool,
108    /// Doc comments on the methods
109    doc_comments: TokenStream,
110    /// Whether self is passed as mutable to the method
111    is_mut: bool,
112    /// The await to append to method calls
113    method_await: TokenStream,
114    /// The typed inputs passed to the method
115    typed_inputs: Vec<PatType>,
116    /// The method arguments' introspection
117    intro_args: TokenStream,
118    /// Whether the output type is a Result
119    is_result_output: bool,
120    /// Code block to deserialize arguments from zbus message
121    args_from_msg: TokenStream,
122    /// Names of all arguments to the method
123    args_names: TokenStream,
124    /// Code stream to match on the reply of the method call
125    reply: TokenStream,
126    /// The signal context object argument
127    signal_emitter_arg: Option<PatType>,
128    /// The name of the method (setters are stripped of set_ prefix)
129    member_name: String,
130    /// The proxy method attributes, if any.
131    proxy_attrs: Option<ProxyMethodAttributes>,
132    /// The method output type.
133    output: ReturnType,
134    /// The cfg attributes of the method.
135    cfg_attrs: Vec<Attribute>,
136    /// The doc attributes of the method.
137    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                        // non #[doc = "..."] attributes are not our concern
168                        // we leave them for rustc to handle
169                        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    // the impl Type
305    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            // Ensure the interface name is valid.
320            (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    // Store parsed information about each method
339    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            // Since signals do not have a function body, they don't parse as ImplItemFn…
344            ImplItem::Verbatim(tokens) => {
345                // … thus parse them ourselves and construct an ImplItemFn from that
346                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                    // This empty block will be replaced below.
354                    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                    // * For reference arg, we convert from `&Value` (so `TryFrom<&Value<'_>>` is
519                    //   required).
520                    //
521                    // * For argument type with lifetimes, we convert from `Value` (so
522                    //   `TryFrom<Value<'_>>` is required).
523                    //
524                    // * For all other arg types, we convert the passed value to `OwnedValue` first
525                    //   and then pass it as `Value` (so `TryFrom<OwnedValue>` is required).
526                    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                    // Use setter argument type as the property type if the getter is not
550                    // explicitly defined.
551                    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
1133// Removes all `zbus` attributes from the given inputs.
1134fn 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                    // Turn into error
1293                    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
1412// Like ImplItemFn, but with a semicolon at the end instead of a body block
1413struct 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    // The type name
1433    ty: Ident,
1434    // The interface name
1435    iface_name: String,
1436    // The zbus crate
1437    zbus: TokenStream,
1438
1439    // Input
1440    attrs: ProxyAttributes,
1441
1442    // Output
1443    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                            // Proxy methods always return `zbus::Result<T>`
1493                            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![pub](ty.span())),
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}