Skip to content

Commit

Permalink
✨ zb,zm: Support special args in interface property setters
Browse files Browse the repository at this point in the history
  • Loading branch information
rzvncj committed Dec 22, 2024
1 parent a431ab2 commit e3e0d11
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 41 deletions.
25 changes: 18 additions & 7 deletions zbus/src/fdo/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<()> {
Expand All @@ -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!(
Expand All @@ -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!(
Expand Down
13 changes: 12 additions & 1 deletion zbus/src/object_server/interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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<fdo::Result<()>>;
Expand Down
17 changes: 17 additions & 0 deletions zbus/tests/e2e.rs
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,22 @@ impl MyIface {
header.is_some()
}

#[instrument]
#[zbus(property)]
fn set_test_header_prop(
&self,
value: bool,
#[zbus(header)] header: Option<Header<'_>>,
#[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<String, String> {
Expand Down Expand Up @@ -585,6 +601,7 @@ async fn my_iface_test(conn: Connection, event: Event) -> zbus::Result<u32> {
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);
Expand Down
89 changes: 61 additions & 28 deletions zbus_macros/src/iface.rs
Original file line number Diff line number Diff line change
Expand Up @@ -521,17 +521,19 @@ pub fn expand(args: Punctuated<Meta, Token![,]>, 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
Expand All @@ -552,8 +554,17 @@ pub fn expand(args: Punctuated<Meta, Token![,]>, 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
{
Expand Down Expand Up @@ -581,6 +592,8 @@ pub fn expand(args: Punctuated<Meta, Token![,]>, 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!({
Expand All @@ -605,9 +618,11 @@ pub fn expand(args: Punctuated<Meta, Token![,]>, 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,
Expand Down Expand Up @@ -893,6 +908,8 @@ pub fn expand(args: Punctuated<Meta, Token![,]>, 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> {
Expand All @@ -906,6 +923,8 @@ pub fn expand(args: Punctuated<Meta, Token![,]>, 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<()>> {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
9 changes: 4 additions & 5 deletions zbus_macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Header<'_>>`, 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<Header<'_>>`,
/// 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.
Expand Down

0 comments on commit e3e0d11

Please sign in to comment.