zbus_macros/
proxy.rs

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