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 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 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 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 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 !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 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 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 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 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 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 pub fn into_inner(self) -> #proxy_struct<'p> {
450 self.0
451 }
452
453 pub fn inner(&self) -> &#proxy_struct<'p> {
455 &self.0
456 }
457
458 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 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 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 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 ::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 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 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 pub fn into_inner(self) -> #zbus::#signal_type<'a> {
1179 self.0
1180 }
1181
1182 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}