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: TokenStream, wait: TokenStream) = 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: &str => Err(syn::Error::new(
667 span,
668 message: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: TypeReference = syn::fold::fold_type_reference(self, node);
792 t.lifetime = Some(syn::Lifetime::new(symbol:"'s", Span::call_site()));
793 t
794 }
795
796 fn fold_lifetime(&mut self, _node: syn::Lifetime) -> syn::Lifetime {
797 syn::Lifetime::new(symbol:"'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}
1162