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