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