From e3e0d1105a12415adf65ef12ac0d685502618dbf Mon Sep 17 00:00:00 2001 From: Razvan Cojocaru Date: Sun, 22 Dec 2024 15:37:55 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20zb,zm:=20Support=20special=20args?= =?UTF-8?q?=20in=20interface=20property=20setters?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- zbus/src/fdo/properties.rs | 25 +++++-- zbus/src/object_server/interface/mod.rs | 13 +++- zbus/tests/e2e.rs | 17 +++++ zbus_macros/src/iface.rs | 89 +++++++++++++++++-------- zbus_macros/src/lib.rs | 9 ++- 5 files changed, 112 insertions(+), 41 deletions(-) diff --git a/zbus/src/fdo/properties.rs b/zbus/src/fdo/properties.rs index 024ea3767..4a81622ab 100644 --- a/zbus/src/fdo/properties.rs +++ b/zbus/src/fdo/properties.rs @@ -56,12 +56,14 @@ impl Properties { } /// Set a property value. + #[allow(clippy::too_many_arguments)] async fn set( &self, interface_name: InterfaceName<'_>, property_name: &str, value: Value<'_>, #[zbus(object_server)] server: &ObjectServer, + #[zbus(connection)] connection: &Connection, #[zbus(header)] header: Header<'_>, #[zbus(signal_emitter)] emitter: SignalEmitter<'_>, ) -> Result<()> { @@ -74,12 +76,14 @@ impl Properties { Error::UnknownInterface(format!("Unknown interface '{interface_name}'")) })?; - match iface - .instance - .read() - .await - .set(property_name, &value, &emitter, Some(&header)) - { + match iface.instance.read().await.set( + property_name, + &value, + server, + connection, + &emitter, + Some(&header), + ) { zbus::object_server::DispatchResult::RequiresMut => {} zbus::object_server::DispatchResult::NotFound => { return Err(Error::UnknownProperty(format!( @@ -94,7 +98,14 @@ impl Properties { .instance .write() .await - .set_mut(property_name, &value, &emitter, Some(&header)) + .set_mut( + property_name, + &value, + server, + connection, + &emitter, + Some(&header), + ) .await; res.unwrap_or_else(|| { Err(Error::UnknownProperty(format!( diff --git a/zbus/src/object_server/interface/mod.rs b/zbus/src/object_server/interface/mod.rs index 3e555517e..9140d9bc2 100644 --- a/zbus/src/object_server/interface/mod.rs +++ b/zbus/src/object_server/interface/mod.rs @@ -78,10 +78,19 @@ pub trait Interface: Any + Send + Sync { &'call self, property_name: &'call str, value: &'call Value<'_>, + object_server: &'call ObjectServer, + connection: &'call Connection, emitter: &'call SignalEmitter<'_>, header: Option<&'call message::Header<'_>>, ) -> DispatchResult<'call> { - let _ = (property_name, value, emitter, header); + let _ = ( + property_name, + value, + object_server, + connection, + emitter, + header, + ); DispatchResult::RequiresMut } @@ -94,6 +103,8 @@ pub trait Interface: Any + Send + Sync { &mut self, property_name: &str, value: &Value<'_>, + object_server: &ObjectServer, + connection: &Connection, emitter: &SignalEmitter<'_>, header: Option<&Header<'_>>, ) -> Option>; diff --git a/zbus/tests/e2e.rs b/zbus/tests/e2e.rs index b987668fb..76a099fb6 100644 --- a/zbus/tests/e2e.rs +++ b/zbus/tests/e2e.rs @@ -277,6 +277,22 @@ impl MyIface { header.is_some() } + #[instrument] + #[zbus(property)] + fn set_test_header_prop( + &self, + value: bool, + #[zbus(header)] header: Option>, + #[zbus(connection)] connection: &Connection, + #[zbus(object_server)] object_server: &ObjectServer, + #[zbus(signal_emitter)] emitter: SignalEmitter<'_>, + ) { + debug!("`TestHeaderProp` setter called, value: {}, header: {:?}, connection: {:?}, object_server: {:?}, emitter: {:?}", + value, header, connection, object_server, emitter + ); + assert!(header.is_some()); + } + #[instrument] #[zbus(property)] async fn hash_map(&self) -> HashMap { @@ -585,6 +601,7 @@ async fn my_iface_test(conn: Connection, event: Event) -> zbus::Result { drop(props_changed_stream); proxy.ping().await?; + proxy.set_test_header_prop(true).await?; assert_eq!(proxy.test_header_prop().await?, true); assert_eq!(proxy.count().await?, 1); assert_eq!(proxy.cached_count()?, None); diff --git a/zbus_macros/src/iface.rs b/zbus_macros/src/iface.rs index 5d6152eb8..3fb13144b 100644 --- a/zbus_macros/src/iface.rs +++ b/zbus_macros/src/iface.rs @@ -521,17 +521,19 @@ pub fn expand(args: Punctuated, mut input: ItemImpl) -> syn::Re p.write = true; let set_call = if is_result_output { - quote!(self.#ident(val)#method_await) + quote!(self.#ident(#args_names)#method_await) } else if is_async { quote!( #zbus::export::futures_util::future::FutureExt::map( - self.#ident(val), + self.#ident(#args_names), ::std::result::Result::Ok, ) .await ) } else { - quote!(::std::result::Result::Ok(self.#ident(val))) + quote!( + ::std::result::Result::Ok(self.#ident(#args_names)) + ) }; // * For reference arg, we convert from `&Value` (so `TryFrom<&Value<'_>>` is @@ -552,8 +554,17 @@ pub fn expand(args: Punctuated, mut input: ItemImpl) -> syn::Re } } }; - let value_arg = match &*typed_inputs - .first() + + let value_param = typed_inputs.iter().find(|input| { + let a = ArgAttributes::parse(&input.attrs).unwrap(); + !a.object_server + && !a.connection + && !a.header + && !a.signal_context + && !a.signal_emitter + }); + + let value_arg = match &*value_param .ok_or_else(|| Error::new_spanned(inputs, "Expected a value argument"))? .ty { @@ -581,6 +592,8 @@ pub fn expand(args: Punctuated, mut input: ItemImpl) -> syn::Re .unwrap_or_else(|| value_to_owned.clone()), _ => value_to_owned, }; + + let value_param_name = &value_param.unwrap().pat; let prop_changed_method = match p.emits_changed_signal { PropertyEmitsChangedSignal::True => { quote!({ @@ -605,9 +618,11 @@ pub fn expand(args: Punctuated, mut input: ItemImpl) -> syn::Re } }; let do_set = quote!({ + #args_from_msg let value = #value_arg; match ::std::convert::TryInto::try_into(value) { ::std::result::Result::Ok(val) => { + let #value_param_name = val; match #set_call { ::std::result::Result::Ok(set_result) => #prop_changed_method e => e, @@ -893,6 +908,8 @@ pub fn expand(args: Punctuated, mut input: ItemImpl) -> syn::Re &'call self, property_name: &'call str, value: &'call #zbus::zvariant::Value<'_>, + object_server: &'call #zbus::ObjectServer, + connection: &'call #zbus::Connection, signal_emitter: &'call #zbus::object_server::SignalEmitter<'_>, header: Option<&'call #zbus::message::Header<'_>>, ) -> #zbus::object_server::DispatchResult<'call> { @@ -906,6 +923,8 @@ pub fn expand(args: Punctuated, mut input: ItemImpl) -> syn::Re &mut self, property_name: &str, value: &#zbus::zvariant::Value<'_>, + object_server: &#zbus::ObjectServer, + connection: &#zbus::Connection, signal_emitter: &#zbus::object_server::SignalEmitter<'_>, header: Option<&#zbus::message::Header<'_>>, ) -> ::std::option::Option<#zbus::fdo::Result<()>> { @@ -1017,7 +1036,9 @@ fn get_args_from_inputs( let header_arg = &input.pat; header_arg_decl = match method_type { - MethodType::Property(_) => Some(quote! { let #header_arg = header.cloned(); }), + MethodType::Property(_) => Some( + quote! { let #header_arg = ::std::option::Option::<&#zbus::message::Header<'_>>::cloned(header); }, + ), _ => Some(quote! { let #header_arg = message.header(); }), }; } else if signal_context || signal_emitter { @@ -1047,35 +1068,47 @@ fn get_args_from_inputs( } } - let args_from_msg = match method_type { - MethodType::Property(_) => quote! { - #server_arg_decl + let (hdr_init, msg_init, signal_emitter_arg_decl, args_decl) = match method_type { + MethodType::Property(PropertyType::Getter) => { + (quote! {}, quote! {}, quote! {}, quote! {}) + } + MethodType::Property(PropertyType::Setter) => ( + quote! { let hdr = header.as_ref().unwrap(); }, + quote! {}, + quote! { #signal_emitter_arg_decl }, + quote! {}, + ), + _ => ( + quote! { let hdr = message.header(); }, + quote! { let msg_body = message.body(); }, + quote! { #signal_emitter_arg_decl }, + quote! { + let (#(#args_names),*): (#(#tys),*) = + match msg_body.deserialize() { + ::std::result::Result::Ok(r) => r, + ::std::result::Result::Err(e) => { + let err = <#zbus::fdo::Error as ::std::convert::From<_>>::from(e); + return connection.reply_dbus_error(&hdr, err).await; + } + }; + }, + ), + }; - #conn_arg_decl + let args_from_msg = quote! { + #hdr_init - #header_arg_decl - }, - _ => quote! { - let hdr = message.header(); - let msg_body = message.body(); + #msg_init - #server_arg_decl + #server_arg_decl - #conn_arg_decl + #conn_arg_decl - #header_arg_decl + #header_arg_decl - #signal_emitter_arg_decl + #signal_emitter_arg_decl - let (#(#args_names),*): (#(#tys),*) = - match msg_body.deserialize() { - ::std::result::Result::Ok(r) => r, - ::std::result::Result::Err(e) => { - let err = <#zbus::fdo::Error as ::std::convert::From<_>>::from(e); - return connection.reply_dbus_error(&hdr, err).await; - } - }; - }, + #args_decl }; let all_args_names = inputs.iter().filter_map(pat_ident); diff --git a/zbus_macros/src/lib.rs b/zbus_macros/src/lib.rs index 6b2ca1c45..4a005ddb7 100644 --- a/zbus_macros/src/lib.rs +++ b/zbus_macros/src/lib.rs @@ -289,17 +289,16 @@ pub fn proxy(attr: TokenStream, item: TokenStream) -> TokenStream { /// using this since it will force all interested peers to fetch the new value and hence result in /// excess traffic on the bus. /// -/// The method and property getter arguments support the following `zbus` attributes: +/// The method arguments support the following `zbus` attributes: /// /// * `object_server` - This marks the method argument to receive a reference to the /// [`ObjectServer`] this method was called by. /// * `connection` - This marks the method argument to receive a reference to the [`Connection`] on /// which the method call was received. /// * `header` - This marks the method argument to receive the message header associated with the -/// D-Bus method call being handled. For property getter methods, this will be an -/// `Option>`, which will be `None` when the function is being called as part of the -/// initial object setup (before it gets registered on the bus), or when we send out property -/// changed notifications. +/// D-Bus method call being handled. For property methods, this will be an `Option>`, +/// which will be set to `None` if the method is called for reasons other than to respond to an +/// external property access. /// * `signal_emitter` - This marks the method argument to receive a [`SignalEmitter`] instance, /// which is needed for emitting signals the easy way. This argument is not available for property /// getters.