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