zbus_macros/
proxy.rs

1use crate::utils::{pat_ident, typed_arg, zbus_path, PropertyEmitsChangedSignal};
2use proc_macro2::{Literal, Span, TokenStream};
3use quote::{format_ident, quote, quote_spanned, ToTokens};
4use syn::{
5    fold::Fold, parse_quote, parse_str, punctuated::Punctuated, spanned::Spanned, Error, FnArg,
6    Ident, ItemTrait, Meta, Path, ReturnType, Token, TraitItemFn,
7};
8use zvariant_utils::{case, def_attrs, macros::AttrParse, old_new};
9
10pub mod old {
11    use super::def_attrs;
12    def_attrs! {
13        crate dbus_proxy;
14
15        pub TraitAttributes("trait") {
16            interface str,
17            name str,
18            assume_defaults bool,
19            default_path str,
20            default_service str,
21            async_name str,
22            blocking_name str,
23            gen_async bool,
24            gen_blocking bool
25        };
26
27        pub MethodAttributes("method") {
28            name str,
29            property {
30                pub PropertyAttributes("property") {
31                    emits_changed_signal str
32                }
33            },
34            signal none,
35            object str,
36            async_object str,
37            blocking_object str,
38            no_reply none,
39            no_autostart none,
40            allow_interactive_auth none
41        };
42    }
43}
44
45def_attrs! {
46    crate zbus;
47
48    // Keep this in sync with interface's proxy method attributes.
49    pub TraitAttributes("trait") {
50        interface str,
51        name str,
52        assume_defaults bool,
53        default_path str,
54        default_service str,
55        async_name str,
56        blocking_name str,
57        gen_async bool,
58        gen_blocking bool
59    };
60
61    // Keep this in sync with interface's proxy method attributes.
62    pub MethodAttributes("method") {
63        name str,
64        property {
65            pub PropertyAttributes("property") {
66                emits_changed_signal str
67            }
68        },
69        signal none,
70        object str,
71        async_object str,
72        blocking_object str,
73        no_reply none,
74        no_autostart none,
75        allow_interactive_auth none
76    };
77}
78
79old_new!(TraitAttrs, old::TraitAttributes, TraitAttributes);
80old_new!(MethodAttrs, old::MethodAttributes, MethodAttributes);
81
82struct AsyncOpts {
83    blocking: bool,
84    usage: TokenStream,
85    wait: TokenStream,
86}
87
88impl AsyncOpts {
89    fn new(blocking: bool) -> Self {
90        let (usage, wait) = if blocking {
91            (quote! {}, quote! {})
92        } else {
93            (quote! { async }, quote! { .await })
94        };
95        Self {
96            blocking,
97            usage,
98            wait,
99        }
100    }
101}
102
103pub fn expand<I: AttrParse + Into<TraitAttrs>, M: AttrParse + Into<MethodAttrs>>(
104    args: Punctuated<Meta, Token![,]>,
105    input: ItemTrait,
106) -> Result<TokenStream, Error> {
107    let (
108        interface,
109        name,
110        assume_defaults,
111        default_path,
112        default_service,
113        async_name,
114        blocking_name,
115        gen_async,
116        gen_blocking,
117    ) = match I::parse_nested_metas(args)?.into() {
118        TraitAttrs::Old(old) => (
119            old.interface,
120            old.name,
121            old.assume_defaults,
122            old.default_path,
123            old.default_service,
124            old.async_name,
125            old.blocking_name,
126            old.gen_async,
127            old.gen_blocking,
128        ),
129        TraitAttrs::New(new) => (
130            new.interface,
131            new.name,
132            new.assume_defaults,
133            new.default_path,
134            new.default_service,
135            new.async_name,
136            new.blocking_name,
137            new.gen_async,
138            new.gen_blocking,
139        ),
140    };
141
142    let iface_name = match (interface, name) {
143        (Some(name), None) | (None, Some(name)) => Ok(Some(name)),
144        (None, None) => Ok(None),
145        (Some(_), Some(_)) => Err(syn::Error::new(
146            input.span(),
147            "both `interface` and `name` attributes shouldn't be specified at the same time",
148        )),
149    }?;
150    let gen_async = gen_async.unwrap_or(true);
151    let gen_blocking = gen_blocking.unwrap_or(true);
152
153    // Some sanity checks
154    assert!(
155        gen_blocking || gen_async,
156        "Can't disable both asynchronous and blocking proxy. 😸",
157    );
158    assert!(
159        gen_blocking || blocking_name.is_none(),
160        "Can't set blocking proxy's name if you disabled it. 😸",
161    );
162    assert!(
163        gen_async || async_name.is_none(),
164        "Can't set asynchronous proxy's name if you disabled it. 😸",
165    );
166
167    let blocking_proxy = if gen_blocking {
168        let proxy_name = blocking_name.unwrap_or_else(|| {
169            if gen_async {
170                format!("{}ProxyBlocking", input.ident)
171            } else {
172                // When only generating blocking proxy, there is no need for a suffix.
173                format!("{}Proxy", input.ident)
174            }
175        });
176        create_proxy::<M>(
177            &input,
178            iface_name.as_deref(),
179            assume_defaults,
180            default_path.as_deref(),
181            default_service.as_deref(),
182            &proxy_name,
183            true,
184            // Signal args structs are shared between the two proxies so always generate it for
185            // async proxy only unless async proxy generation is disabled.
186            !gen_async,
187        )?
188    } else {
189        quote! {}
190    };
191    let async_proxy = if gen_async {
192        let proxy_name = async_name.unwrap_or_else(|| format!("{}Proxy", input.ident));
193        create_proxy::<M>(
194            &input,
195            iface_name.as_deref(),
196            assume_defaults,
197            default_path.as_deref(),
198            default_service.as_deref(),
199            &proxy_name,
200            false,
201            true,
202        )?
203    } else {
204        quote! {}
205    };
206
207    Ok(quote! {
208        #blocking_proxy
209
210        #async_proxy
211    })
212}
213
214#[allow(clippy::too_many_arguments)]
215pub fn create_proxy<M: AttrParse + Into<MethodAttrs>>(
216    input: &ItemTrait,
217    iface_name: Option<&str>,
218    assume_defaults: Option<bool>,
219    default_path: Option<&str>,
220    default_service: Option<&str>,
221    proxy_name: &str,
222    blocking: bool,
223    gen_sig_args: bool,
224) -> Result<TokenStream, Error> {
225    let zbus = zbus_path();
226
227    let other_attrs: Vec<_> = input
228        .attrs
229        .iter()
230        .filter(|a| !a.path().is_ident("zbus") && !a.path().is_ident("dbus_proxy"))
231        .collect();
232    let proxy_name = Ident::new(proxy_name, Span::call_site());
233    let ident = input.ident.to_string();
234    let iface_name = iface_name
235        .map(ToString::to_string)
236        .unwrap_or(format!("org.freedesktop.{ident}"));
237    let assume_defaults = assume_defaults.unwrap_or(false);
238    let (default_path, default_service) = if assume_defaults {
239        let path = default_path
240            .map(ToString::to_string)
241            .or_else(|| Some(format!("/org/freedesktop/{ident}")));
242        let svc = default_service
243            .map(ToString::to_string)
244            .or_else(|| Some(iface_name.clone()));
245        (path, svc)
246    } else {
247        let path = default_path.map(ToString::to_string);
248        let svc = default_service.map(ToString::to_string);
249        (path, svc)
250    };
251    let mut methods = TokenStream::new();
252    let mut stream_types = TokenStream::new();
253    let mut has_properties = false;
254    let mut uncached_properties: Vec<String> = vec![];
255
256    let async_opts = AsyncOpts::new(blocking);
257
258    for i in input.items.iter() {
259        if let syn::TraitItem::Fn(m) = i {
260            let (mut name, signal, property) = match <M>::parse(&m.attrs)?.into() {
261                MethodAttrs::Old(old) => (
262                    old.name,
263                    old.signal,
264                    old.property.map(|property| property.emits_changed_signal),
265                ),
266                MethodAttrs::New(new) => (
267                    new.name,
268                    new.signal,
269                    new.property.map(|property| property.emits_changed_signal),
270                ),
271            };
272
273            let method_name = m.sig.ident.to_string();
274
275            let is_signal = signal;
276            let is_property = property.is_some();
277            let has_inputs = m.sig.inputs.len() > 1;
278
279            let member_name = name.take().unwrap_or_else(|| {
280                case::pascal_or_camel_case(
281                    if is_property && has_inputs {
282                        assert!(method_name.starts_with("set_"));
283                        &method_name[4..]
284                    } else {
285                        &method_name
286                    },
287                    true,
288                )
289            });
290
291            let m = if let Some(prop_attrs) = &property {
292                has_properties = true;
293
294                let emits_changed_signal = if let Some(s) = &prop_attrs {
295                    PropertyEmitsChangedSignal::parse(s, m.span())?
296                } else {
297                    PropertyEmitsChangedSignal::True
298                };
299
300                if let PropertyEmitsChangedSignal::False = emits_changed_signal {
301                    uncached_properties.push(member_name.clone());
302                }
303
304                gen_proxy_property(
305                    &member_name,
306                    &method_name,
307                    m,
308                    &async_opts,
309                    emits_changed_signal,
310                )
311            } else if is_signal {
312                let (method, types) = gen_proxy_signal(
313                    &proxy_name,
314                    &iface_name,
315                    &member_name,
316                    &method_name,
317                    m,
318                    &async_opts,
319                    gen_sig_args,
320                );
321                stream_types.extend(types);
322
323                method
324            } else {
325                gen_proxy_method_call::<M>(
326                    &member_name,
327                    &method_name,
328                    m,
329                    <M>::parse(&m.attrs)?,
330                    &async_opts,
331                )?
332            };
333            methods.extend(m);
334        }
335    }
336
337    let AsyncOpts { usage, wait, .. } = async_opts;
338    let (proxy_struct, connection, builder, proxy_trait) = if blocking {
339        let connection = quote! { #zbus::blocking::Connection };
340        let proxy = quote! { #zbus::blocking::Proxy };
341        let builder = quote! { #zbus::blocking::proxy::Builder };
342        let proxy_trait = quote! { #zbus::blocking::proxy::ProxyImpl };
343
344        (proxy, connection, builder, proxy_trait)
345    } else {
346        let connection = quote! { #zbus::Connection };
347        let proxy = quote! { #zbus::Proxy };
348        let builder = quote! { #zbus::proxy::Builder };
349        let proxy_trait = quote! { #zbus::proxy::ProxyImpl };
350
351        (proxy, connection, builder, proxy_trait)
352    };
353
354    let proxy_method_new = match (&default_path, &default_service) {
355        (None, None) => {
356            quote! {
357                /// Creates a new proxy with the given service destination and path.
358                pub #usage fn new<D, P>(conn: &#connection, destination: D, path: P) -> #zbus::Result<#proxy_name<'p>>
359                where
360                    D: ::std::convert::TryInto<#zbus::names::BusName<'static>>,
361                    D::Error: ::std::convert::Into<#zbus::Error>,
362                    P: ::std::convert::TryInto<#zbus::zvariant::ObjectPath<'static>>,
363                    P::Error: ::std::convert::Into<#zbus::Error>,
364                {
365                    let obj_path = path.try_into().map_err(::std::convert::Into::into)?;
366                    let obj_destination = destination.try_into().map_err(::std::convert::Into::into)?;
367                    Self::builder(conn)
368                        .path(obj_path)?
369                        .destination(obj_destination)?
370                        .build()#wait
371                }
372            }
373        }
374        (Some(_), None) => {
375            quote! {
376                /// Creates a new proxy with the given destination, and the default path.
377                pub #usage fn new<D>(conn: &#connection, destination: D) -> #zbus::Result<#proxy_name<'p>>
378                where
379                    D: ::std::convert::TryInto<#zbus::names::BusName<'static>>,
380                    D::Error: ::std::convert::Into<#zbus::Error>,
381                {
382                    let obj_dest = destination.try_into().map_err(::std::convert::Into::into)?;
383                    Self::builder(conn)
384                        .destination(obj_dest)?
385                        .build()#wait
386                }
387            }
388        }
389        (None, Some(_)) => {
390            quote! {
391                /// Creates a new proxy with the given path, and the default destination.
392                pub #usage fn new<P>(conn: &#connection, path: P) -> #zbus::Result<#proxy_name<'p>>
393                where
394                    P: ::std::convert::TryInto<#zbus::zvariant::ObjectPath<'static>>,
395                    P::Error: ::std::convert::Into<#zbus::Error>,
396                {
397                    let obj_path = path.try_into().map_err(::std::convert::Into::into)?;
398                    Self::builder(conn)
399                        .path(obj_path)?
400                        .build()#wait
401                }
402            }
403        }
404        (Some(_), Some(_)) => {
405            quote! {
406                /// Creates a new proxy with the default service and path.
407                pub #usage fn new(conn: &#connection) -> #zbus::Result<#proxy_name<'p>> {
408                    Self::builder(conn).build()#wait
409                }
410            }
411        }
412    };
413    let default_path = match default_path {
414        Some(p) => quote! { Some(#p) },
415        None => quote! { None },
416    };
417    let default_service = match default_service {
418        Some(d) => quote! { Some(#d) },
419        None => quote! { None },
420    };
421
422    Ok(quote! {
423        impl<'a> #zbus::proxy::ProxyDefault for #proxy_name<'a> {
424            const INTERFACE: Option<&'static str> = Some(#iface_name);
425            const DESTINATION: Option<&'static str> = #default_service;
426            const PATH: Option<&'static str> = #default_path;
427        }
428
429        #(#other_attrs)*
430        #[derive(Clone, Debug)]
431        pub struct #proxy_name<'p>(#proxy_struct<'p>);
432
433        impl<'p> #proxy_name<'p> {
434            #proxy_method_new
435
436            /// Returns a customizable builder for this proxy.
437            pub fn builder(conn: &#connection) -> #builder<'p, Self> {
438                let mut builder = #builder::new(conn) ;
439                if #has_properties {
440                    let uncached = vec![#(#uncached_properties),*];
441                    builder.cache_properties(#zbus::proxy::CacheProperties::default())
442                           .uncached_properties(&uncached)
443                } else {
444                    builder.cache_properties(#zbus::proxy::CacheProperties::No)
445                }
446            }
447
448            /// Consumes `self`, returning the underlying `zbus::Proxy`.
449            pub fn into_inner(self) -> #proxy_struct<'p> {
450                self.0
451            }
452
453            /// The reference to the underlying `zbus::Proxy`.
454            pub fn inner(&self) -> &#proxy_struct<'p> {
455                &self.0
456            }
457
458            /// The mutable reference to the underlying `zbus::Proxy`.
459            pub fn inner_mut(&mut self) -> &mut #proxy_struct<'p> {
460                &mut self.0
461            }
462
463            #methods
464        }
465
466        impl<'p> #proxy_trait<'p> for #proxy_name<'p> {
467            fn builder(conn: &#connection) -> #builder<'p, Self> {
468                Self::builder(conn)
469            }
470
471            fn into_inner(self) -> #proxy_struct<'p> {
472                self.into_inner()
473            }
474
475            fn inner(&self) -> &#proxy_struct<'p> {
476                self.inner()
477            }
478        }
479
480        impl<'p> ::std::convert::From<#zbus::Proxy<'p>> for #proxy_name<'p> {
481            fn from(proxy: #zbus::Proxy<'p>) -> Self {
482                #proxy_name(::std::convert::Into::into(proxy))
483            }
484        }
485
486        impl<'p> ::std::convert::AsRef<#proxy_struct<'p>> for #proxy_name<'p> {
487            fn as_ref(&self) -> &#proxy_struct<'p> {
488                self.inner()
489            }
490        }
491
492        impl<'p> ::std::convert::AsMut<#proxy_struct<'p>> for #proxy_name<'p> {
493            fn as_mut(&mut self) -> &mut #proxy_struct<'p> {
494                self.inner_mut()
495            }
496        }
497
498        impl<'p> #zbus::zvariant::Type for #proxy_name<'p> {
499            fn signature() -> #zbus::zvariant::Signature<'static> {
500                #zbus::zvariant::OwnedObjectPath::signature()
501            }
502        }
503
504        impl<'p> #zbus::export::serde::ser::Serialize for #proxy_name<'p> {
505            fn serialize<S>(&self, serializer: S) -> ::std::result::Result<S::Ok, S::Error>
506            where
507                S: #zbus::export::serde::ser::Serializer,
508            {
509                ::std::string::String::serialize(
510                    &::std::string::ToString::to_string(self.inner().path()),
511                    serializer,
512                )
513            }
514        }
515
516        #stream_types
517    })
518}
519
520fn gen_proxy_method_call<M: AttrParse + Into<MethodAttrs>>(
521    method_name: &str,
522    snake_case_name: &str,
523    m: &TraitItemFn,
524    method_attrs: M,
525    async_opts: &AsyncOpts,
526) -> Result<TokenStream, Error> {
527    let (object, blocking_object, async_object, no_reply, no_autostart, allow_interactive_auth) =
528        match method_attrs.into() {
529            MethodAttrs::Old(old) => (
530                old.object,
531                old.blocking_object,
532                old.async_object,
533                old.no_reply,
534                old.no_autostart,
535                old.allow_interactive_auth,
536            ),
537            MethodAttrs::New(new) => (
538                new.object,
539                new.blocking_object,
540                new.async_object,
541                new.no_reply,
542                new.no_autostart,
543                new.allow_interactive_auth,
544            ),
545        };
546    let AsyncOpts {
547        usage,
548        wait,
549        blocking,
550    } = async_opts;
551    let zbus = zbus_path();
552    let other_attrs: Vec<_> = m
553        .attrs
554        .iter()
555        .filter(|a| !a.path().is_ident("zbus") && !a.path().is_ident("dbus_proxy"))
556        .collect();
557    let args: Vec<_> = m
558        .sig
559        .inputs
560        .iter()
561        .filter_map(typed_arg)
562        .filter_map(pat_ident)
563        .collect();
564
565    let proxy_object = object.as_ref().map(|o| {
566        if *blocking {
567            // FIXME: for some reason Rust doesn't let us move `blocking_proxy_object` so we've to
568            // clone.
569            blocking_object
570                .as_ref()
571                .cloned()
572                .unwrap_or_else(|| format!("{o}ProxyBlocking"))
573        } else {
574            async_object
575                .as_ref()
576                .cloned()
577                .unwrap_or_else(|| format!("{o}Proxy"))
578        }
579    });
580
581    let method_flags = match (no_reply, no_autostart, allow_interactive_auth) {
582        (true, false, false) => Some(quote!(::std::convert::Into::into(
583            zbus::proxy::MethodFlags::NoReplyExpected
584        ))),
585        (false, true, false) => Some(quote!(::std::convert::Into::into(
586            zbus::proxy::MethodFlags::NoAutoStart
587        ))),
588        (false, false, true) => Some(quote!(::std::convert::Into::into(
589            zbus::proxy::MethodFlags::AllowInteractiveAuth
590        ))),
591
592        (true, true, false) => Some(quote!(
593            zbus::proxy::MethodFlags::NoReplyExpected | zbus::proxy::MethodFlags::NoAutoStart
594        )),
595        (true, false, true) => Some(quote!(
596            zbus::proxy::MethodFlags::NoReplyExpected
597                | zbus::proxy::MethodFlags::AllowInteractiveAuth
598        )),
599        (false, true, true) => Some(quote!(
600            zbus::proxy::MethodFlags::NoAutoStart | zbus::proxy::MethodFlags::AllowInteractiveAuth
601        )),
602
603        (true, true, true) => Some(quote!(
604            zbus::proxy::MethodFlags::NoReplyExpected
605                | zbus::proxy::MethodFlags::NoAutoStart
606                | zbus::proxy::MethodFlags::AllowInteractiveAuth
607        )),
608        _ => None,
609    };
610
611    let method = Ident::new(snake_case_name, Span::call_site());
612    let inputs = &m.sig.inputs;
613    let mut generics = m.sig.generics.clone();
614    let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
615    for param in generics
616        .params
617        .iter()
618        .filter(|a| matches!(a, syn::GenericParam::Type(_)))
619    {
620        let is_input_type = inputs.iter().any(|arg| {
621            // FIXME: We want to only require `Serialize` from input types and `DeserializeOwned`
622            // from output types but since we don't have type introspection, we employ this
623            // workaround of matching on the string representation of the the types to figure out
624            // which generic types are input types.
625            if let FnArg::Typed(pat) = arg {
626                let pat = pat.ty.to_token_stream().to_string();
627
628                if let Some(ty_name) = pat.strip_prefix('&') {
629                    let ty_name = ty_name.trim_start();
630
631                    ty_name == param.to_token_stream().to_string()
632                } else {
633                    false
634                }
635            } else {
636                false
637            }
638        });
639        let serde_bound: TokenStream = if is_input_type {
640            parse_quote!(#zbus::export::serde::ser::Serialize)
641        } else {
642            parse_quote!(#zbus::export::serde::de::DeserializeOwned)
643        };
644        where_clause.predicates.push(parse_quote!(
645            #param: #serde_bound + #zbus::zvariant::Type
646        ));
647    }
648    let (_, ty_generics, where_clause) = generics.split_for_impl();
649
650    if let Some(proxy_path) = proxy_object {
651        let proxy_path = parse_str::<Path>(&proxy_path)?;
652        let signature = quote! {
653            fn #method #ty_generics(#inputs) -> #zbus::Result<#proxy_path<'p>>
654            #where_clause
655        };
656
657        Ok(quote! {
658            #(#other_attrs)*
659            pub #usage #signature {
660                let object_path: #zbus::zvariant::OwnedObjectPath =
661                    self.0.call(
662                        #method_name,
663                        &#zbus::zvariant::DynamicTuple((#(#args,)*)),
664                    )
665                    #wait?;
666                #proxy_path::builder(&self.0.connection())
667                    .path(object_path)?
668                    .build()
669                    #wait
670            }
671        })
672    } else {
673        let body = if args.len() == 1 {
674            // Wrap single arg in a tuple so if it's a struct/tuple itself, zbus will only remove
675            // the '()' from the signature that we add and not the actual intended ones.
676            let arg = &args[0];
677            quote! {
678                &#zbus::zvariant::DynamicTuple((#arg,))
679            }
680        } else {
681            quote! {
682                &#zbus::zvariant::DynamicTuple((#(#args),*))
683            }
684        };
685
686        let output = &m.sig.output;
687        let signature = quote! {
688            fn #method #ty_generics(#inputs) #output
689            #where_clause
690        };
691
692        if let Some(method_flags) = method_flags {
693            if no_reply {
694                Ok(quote! {
695                    #(#other_attrs)*
696                    pub #usage #signature {
697                        self.0.call_with_flags::<_, _, ()>(#method_name, #method_flags, #body)#wait?;
698                        ::std::result::Result::Ok(())
699                    }
700                })
701            } else {
702                Ok(quote! {
703                    #(#other_attrs)*
704                    pub #usage #signature {
705                        let reply = self.0.call_with_flags(#method_name, #method_flags, #body)#wait?;
706
707                        // SAFETY: This unwrap() cannot fail due to the guarantees in
708                        // call_with_flags, which can only return Ok(None) if the
709                        // NoReplyExpected is set. By not passing NoReplyExpected,
710                        // we are guaranteed to get either an Err variant (handled
711                        // in the previous statement) or Ok(Some(T)) which is safe to
712                        // unwrap
713                        ::std::result::Result::Ok(reply.unwrap())
714                    }
715                })
716            }
717        } else {
718            Ok(quote! {
719                #(#other_attrs)*
720                pub #usage #signature {
721                    let reply = self.0.call(#method_name, #body)#wait?;
722                    ::std::result::Result::Ok(reply)
723                }
724            })
725        }
726    }
727}
728
729fn gen_proxy_property(
730    property_name: &str,
731    method_name: &str,
732    m: &TraitItemFn,
733    async_opts: &AsyncOpts,
734    emits_changed_signal: PropertyEmitsChangedSignal,
735) -> TokenStream {
736    let AsyncOpts {
737        usage,
738        wait,
739        blocking,
740    } = async_opts;
741    let zbus = zbus_path();
742    let other_attrs: Vec<_> = m
743        .attrs
744        .iter()
745        .filter(|a| !a.path().is_ident("zbus") && !a.path().is_ident("dbus_proxy"))
746        .collect();
747    let signature = &m.sig;
748    if signature.inputs.len() > 1 {
749        let value = pat_ident(typed_arg(signature.inputs.last().unwrap()).unwrap()).unwrap();
750        quote! {
751            #(#other_attrs)*
752            #[allow(clippy::needless_question_mark)]
753            pub #usage #signature {
754                ::std::result::Result::Ok(self.0.set_property(#property_name, #value)#wait?)
755            }
756        }
757    } else {
758        // This should fail to compile only if the return type is wrong,
759        // so use that as the span.
760        let body_span = if let ReturnType::Type(_, ty) = &signature.output {
761            ty.span()
762        } else {
763            signature.span()
764        };
765        let body = quote_spanned! {body_span =>
766            ::std::result::Result::Ok(self.0.get_property(#property_name)#wait?)
767        };
768        let ret_type = if let ReturnType::Type(_, ty) = &signature.output {
769            Some(ty)
770        } else {
771            None
772        };
773
774        let (proxy_name, prop_stream) = if *blocking {
775            (
776                "zbus::blocking::Proxy",
777                quote! { #zbus::blocking::proxy::PropertyIterator },
778            )
779        } else {
780            ("zbus::Proxy", quote! { #zbus::proxy::PropertyStream })
781        };
782
783        let receive_method = match emits_changed_signal {
784            PropertyEmitsChangedSignal::True | PropertyEmitsChangedSignal::Invalidates => {
785                let (_, ty_generics, where_clause) = m.sig.generics.split_for_impl();
786                let receive = format_ident!("receive_{}_changed", method_name);
787                let gen_doc = format!(
788                    "Create a stream for the `{property_name}` property changes. \
789                This is a convenient wrapper around [`{proxy_name}::receive_property_changed`]."
790                );
791                quote! {
792                    #[doc = #gen_doc]
793                    pub #usage fn #receive #ty_generics(
794                        &self
795                    ) -> #prop_stream<'p, <#ret_type as #zbus::ResultAdapter>::Ok>
796                    #where_clause
797                    {
798                        self.0.receive_property_changed(#property_name)#wait
799                    }
800                }
801            }
802            PropertyEmitsChangedSignal::False | PropertyEmitsChangedSignal::Const => {
803                quote! {}
804            }
805        };
806
807        let cached_getter_method = match emits_changed_signal {
808            PropertyEmitsChangedSignal::True
809            | PropertyEmitsChangedSignal::Invalidates
810            | PropertyEmitsChangedSignal::Const => {
811                let cached_getter = format_ident!("cached_{}", method_name);
812                let cached_doc = format!(
813                    " Get the cached value of the `{property_name}` property, or `None` if the property is not cached.",
814                );
815                quote! {
816                    #[doc = #cached_doc]
817                    pub fn #cached_getter(&self) -> ::std::result::Result<
818                        ::std::option::Option<<#ret_type as #zbus::ResultAdapter>::Ok>,
819                        <#ret_type as #zbus::ResultAdapter>::Err>
820                    {
821                        self.0.cached_property(#property_name).map_err(::std::convert::Into::into)
822                    }
823                }
824            }
825            PropertyEmitsChangedSignal::False => quote! {},
826        };
827
828        quote! {
829            #(#other_attrs)*
830            #[allow(clippy::needless_question_mark)]
831            pub #usage #signature {
832                #body
833            }
834
835            #cached_getter_method
836
837            #receive_method
838        }
839    }
840}
841
842struct SetLifetimeS;
843
844impl Fold for SetLifetimeS {
845    fn fold_type_reference(&mut self, node: syn::TypeReference) -> syn::TypeReference {
846        let mut t = syn::fold::fold_type_reference(self, node);
847        t.lifetime = Some(syn::Lifetime::new("'s", Span::call_site()));
848        t
849    }
850
851    fn fold_lifetime(&mut self, _node: syn::Lifetime) -> syn::Lifetime {
852        syn::Lifetime::new("'s", Span::call_site())
853    }
854}
855
856fn gen_proxy_signal(
857    proxy_name: &Ident,
858    iface_name: &str,
859    signal_name: &str,
860    snake_case_name: &str,
861    method: &TraitItemFn,
862    async_opts: &AsyncOpts,
863    gen_sig_args: bool,
864) -> (TokenStream, TokenStream) {
865    let AsyncOpts {
866        usage,
867        wait,
868        blocking,
869    } = async_opts;
870    let zbus = zbus_path();
871    let other_attrs: Vec<_> = method
872        .attrs
873        .iter()
874        .filter(|a| !a.path().is_ident("zbus") && !a.path().is_ident("dbus_proxy"))
875        .collect();
876    let input_types: Vec<_> = method
877        .sig
878        .inputs
879        .iter()
880        .filter_map(|arg| match arg {
881            FnArg::Typed(p) => Some(&*p.ty),
882            _ => None,
883        })
884        .collect();
885    let input_types_s: Vec<_> = SetLifetimeS
886        .fold_signature(method.sig.clone())
887        .inputs
888        .iter()
889        .filter_map(|arg| match arg {
890            FnArg::Typed(p) => Some(p.ty.clone()),
891            _ => None,
892        })
893        .collect();
894    let args: Vec<Ident> = method
895        .sig
896        .inputs
897        .iter()
898        .filter_map(typed_arg)
899        .filter_map(|arg| pat_ident(arg).cloned())
900        .collect();
901    let args_nth: Vec<Literal> = args
902        .iter()
903        .enumerate()
904        .map(|(i, _)| Literal::usize_unsuffixed(i))
905        .collect();
906
907    let mut generics = method.sig.generics.clone();
908    let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
909    for param in generics
910        .params
911        .iter()
912        .filter(|a| matches!(a, syn::GenericParam::Type(_)))
913    {
914        where_clause
915                .predicates
916                .push(parse_quote!(#param: #zbus::export::serde::de::Deserialize<'s> + #zbus::zvariant::Type + ::std::fmt::Debug));
917    }
918    generics.params.push(parse_quote!('s));
919    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
920
921    let (
922        proxy_path,
923        receive_signal_link,
924        receive_signal_with_args_link,
925        trait_name,
926        trait_link,
927        signal_type,
928    ) = if *blocking {
929        (
930            "zbus::blocking::Proxy",
931            "https://docs.rs/zbus/latest/zbus/blocking/proxy/struct.Proxy.html#method.receive_signal",
932            "https://docs.rs/zbus/latest/zbus/blocking/proxy/struct.Proxy.html#method.receive_signal_with_args",
933            "Iterator",
934            "https://doc.rust-lang.org/std/iter/trait.Iterator.html",
935            quote! { blocking::proxy::SignalIterator },
936        )
937    } else {
938        (
939            "zbus::Proxy",
940            "https://docs.rs/zbus/latest/zbus/proxy/struct.Proxy.html#method.receive_signal",
941            "https://docs.rs/zbus/latest/zbus/proxy/struct.Proxy.html#method.receive_signal_with_args",
942            "Stream",
943            "https://docs.rs/futures/0.3.15/futures/stream/trait.Stream.html",
944            quote! { proxy::SignalStream },
945        )
946    };
947    let receiver_name = format_ident!("receive_{snake_case_name}");
948    let receiver_with_args_name = format_ident!("receive_{snake_case_name}_with_args");
949    let stream_name = format_ident!("{signal_name}{trait_name}");
950    let signal_args = format_ident!("{signal_name}Args");
951    let signal_name_ident = format_ident!("{signal_name}");
952
953    let receive_gen_doc = format!(
954        "Create a stream that receives `{signal_name}` signals.\n\
955            \n\
956            This a convenient wrapper around [`{proxy_path}::receive_signal`]({receive_signal_link}).",
957    );
958    let receive_with_args_gen_doc = format!(
959        "Create a stream that receives `{signal_name}` signals.\n\
960            \n\
961            This a convenient wrapper around [`{proxy_path}::receive_signal_with_args`]({receive_signal_with_args_link}).",
962    );
963    let receive_signal_with_args = if args.is_empty() {
964        quote!()
965    } else {
966        quote! {
967            #[doc = #receive_with_args_gen_doc]
968            #(#other_attrs)*
969            pub #usage fn #receiver_with_args_name(&self, args: &[(u8, &str)]) -> #zbus::Result<#stream_name<'static>>
970            {
971                self.0.receive_signal_with_args(#signal_name, args)#wait.map(#stream_name)
972            }
973        }
974    };
975    let receive_signal = quote! {
976        #[doc = #receive_gen_doc]
977        #(#other_attrs)*
978        pub #usage fn #receiver_name(&self) -> #zbus::Result<#stream_name<'static>>
979        {
980            self.0.receive_signal(#signal_name)#wait.map(#stream_name)
981        }
982
983        #receive_signal_with_args
984    };
985
986    let stream_gen_doc = format!(
987        "A [`{trait_name}`] implementation that yields [`{signal_name}`] signals.\n\
988            \n\
989            Use [`{proxy_name}::{receiver_name}`] to create an instance of this type.\n\
990            \n\
991            [`{trait_name}`]: {trait_link}",
992    );
993    let signal_args_gen_doc = format!("`{signal_name}` signal arguments.");
994    let args_struct_gen_doc = format!("A `{signal_name}` signal.");
995    let args_struct_decl = if gen_sig_args {
996        quote! {
997            #[doc = #args_struct_gen_doc]
998            #[derive(Debug, Clone)]
999            pub struct #signal_name_ident(#zbus::message::Body);
1000
1001            impl #signal_name_ident {
1002                #[doc = "Try to construct a "]
1003                #[doc = #signal_name]
1004                #[doc = " from a [`zbus::Message`]."]
1005                pub fn from_message<M>(msg: M) -> ::std::option::Option<Self>
1006                where
1007                    M: ::std::convert::Into<#zbus::message::Message>,
1008                {
1009                    let msg = msg.into();
1010                    let hdr = msg.header();
1011                    let message_type = msg.message_type();
1012                    let interface = hdr.interface();
1013                    let member = hdr.member();
1014                    let interface = interface.as_ref().map(|i| i.as_str());
1015                    let member = member.as_ref().map(|m| m.as_str());
1016
1017                    match (message_type, interface, member) {
1018                        (#zbus::message::Type::Signal, Some(#iface_name), Some(#signal_name)) => {
1019                            Some(Self(msg.body()))
1020                        }
1021                        _ => None,
1022                    }
1023                }
1024
1025                #[doc = "The reference to the underlying [`zbus::Message`]."]
1026                pub fn message(&self) -> &#zbus::message::Message {
1027                    self.0.message()
1028                }
1029            }
1030
1031            impl ::std::convert::From<#signal_name_ident> for #zbus::message::Message {
1032                fn from(signal: #signal_name_ident) -> Self {
1033                    signal.0.message().clone()
1034                }
1035            }
1036        }
1037    } else {
1038        quote!()
1039    };
1040    let args_impl = if args.is_empty() || !gen_sig_args {
1041        quote!()
1042    } else {
1043        let arg_fields_init = if args.len() == 1 {
1044            quote! { #(#args)*: args }
1045        } else {
1046            quote! { #(#args: args.#args_nth),* }
1047        };
1048
1049        quote! {
1050            impl #signal_name_ident {
1051                /// Retrieve the signal arguments.
1052                pub fn args #ty_generics(&'s self) -> #zbus::Result<#signal_args #ty_generics>
1053                #where_clause
1054                {
1055                    ::std::convert::TryFrom::try_from(&self.0)
1056                }
1057            }
1058
1059            #[doc = #signal_args_gen_doc]
1060            pub struct #signal_args #ty_generics {
1061                phantom: std::marker::PhantomData<&'s ()>,
1062                #(
1063                    pub #args: #input_types_s
1064                 ),*
1065            }
1066
1067            impl #impl_generics #signal_args #ty_generics
1068                #where_clause
1069            {
1070                #(
1071                    pub fn #args(&self) -> &#input_types_s {
1072                        &self.#args
1073                    }
1074                 )*
1075            }
1076
1077            impl #impl_generics std::fmt::Debug for #signal_args #ty_generics
1078                #where_clause
1079            {
1080                fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1081                    f.debug_struct(#signal_name)
1082                    #(
1083                     .field(stringify!(#args), &self.#args)
1084                    )*
1085                     .finish()
1086                }
1087            }
1088
1089            impl #impl_generics ::std::convert::TryFrom<&'s #zbus::message::Body> for #signal_args #ty_generics
1090                #where_clause
1091            {
1092                type Error = #zbus::Error;
1093
1094                fn try_from(msg_body: &'s #zbus::message::Body) -> #zbus::Result<Self> {
1095                    msg_body.deserialize::<(#(#input_types),*)>()
1096                        .map_err(::std::convert::Into::into)
1097                        .map(|args| {
1098                            #signal_args {
1099                                phantom: ::std::marker::PhantomData,
1100                                #arg_fields_init
1101                            }
1102                        })
1103                }
1104            }
1105        }
1106    };
1107    let stream_impl = if *blocking {
1108        quote! {
1109            impl ::std::iter::Iterator for #stream_name<'_> {
1110                type Item = #signal_name_ident;
1111
1112                fn next(&mut self) -> ::std::option::Option<Self::Item> {
1113                    ::std::iter::Iterator::next(&mut self.0)
1114                        .map(|msg| #signal_name_ident(msg.body()))
1115                }
1116            }
1117        }
1118    } else {
1119        quote! {
1120            impl #zbus::export::futures_core::stream::Stream for #stream_name<'_> {
1121                type Item = #signal_name_ident;
1122
1123                fn poll_next(
1124                    self: ::std::pin::Pin<&mut Self>,
1125                    cx: &mut ::std::task::Context<'_>,
1126                    ) -> ::std::task::Poll<::std::option::Option<Self::Item>> {
1127                    #zbus::export::futures_core::stream::Stream::poll_next(
1128                        ::std::pin::Pin::new(&mut self.get_mut().0),
1129                        cx,
1130                    )
1131                    .map(|msg| msg.map(|msg| #signal_name_ident(msg.body())))
1132                }
1133            }
1134
1135            impl #zbus::export::ordered_stream::OrderedStream for #stream_name<'_> {
1136                type Data = #signal_name_ident;
1137                type Ordering = #zbus::message::Sequence;
1138
1139                fn poll_next_before(
1140                    self: ::std::pin::Pin<&mut Self>,
1141                    cx: &mut ::std::task::Context<'_>,
1142                    before: ::std::option::Option<&Self::Ordering>
1143                    ) -> ::std::task::Poll<#zbus::export::ordered_stream::PollResult<Self::Ordering, Self::Data>> {
1144                    #zbus::export::ordered_stream::OrderedStream::poll_next_before(
1145                        ::std::pin::Pin::new(&mut self.get_mut().0),
1146                        cx,
1147                        before,
1148                    )
1149                    .map(|msg| msg.map_data(|msg| #signal_name_ident(msg.body())))
1150                }
1151            }
1152
1153            impl #zbus::export::futures_core::stream::FusedStream for #stream_name<'_> {
1154                fn is_terminated(&self) -> bool {
1155                    self.0.is_terminated()
1156                }
1157            }
1158
1159            #[#zbus::export::async_trait::async_trait]
1160            impl #zbus::AsyncDrop for #stream_name<'_> {
1161                async fn async_drop(self) {
1162                    self.0.async_drop().await
1163                }
1164            }
1165        }
1166    };
1167    let stream_types = quote! {
1168        #[doc = #stream_gen_doc]
1169        #[derive(Debug)]
1170        pub struct #stream_name<'a>(#zbus::#signal_type<'a>);
1171
1172        #zbus::export::static_assertions::assert_impl_all!(
1173            #stream_name<'_>: ::std::marker::Send, ::std::marker::Unpin
1174        );
1175
1176        impl<'a> #stream_name<'a> {
1177            /// Consumes `self`, returning the underlying `zbus::#signal_type`.
1178            pub fn into_inner(self) -> #zbus::#signal_type<'a> {
1179                self.0
1180            }
1181
1182            /// The reference to the underlying `zbus::#signal_type`.
1183            pub fn inner(&self) -> & #zbus::#signal_type<'a> {
1184                &self.0
1185            }
1186        }
1187
1188        #stream_impl
1189
1190        #args_struct_decl
1191
1192        #args_impl
1193    };
1194
1195    (receive_signal, stream_types)
1196}