Skip to content

Commit

Permalink
Merge pull request #1179 from rzvncj/attributes-in-property-setters
Browse files Browse the repository at this point in the history
✨ zb,zm: Support special args in interface property setters
  • Loading branch information
zeenix authored Dec 22, 2024
2 parents ae2c765 + 1c527b1 commit 279b7a7
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 48 deletions.
19 changes: 15 additions & 4 deletions zbus/src/fdo/properties.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl Properties {
.instance
.read()
.await
.get(property_name, server, conn, &Some(header))
.get(property_name, server, conn, Some(&header))
.await;
res.unwrap_or_else(|| {
Err(Error::UnknownProperty(format!(
Expand All @@ -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 @@ -77,8 +79,10 @@ impl Properties {
match iface.instance.read().await.set(
property_name,
&value,
server,
connection,
&emitter,
&Some(header.clone()),
Some(&header),
) {
zbus::object_server::DispatchResult::RequiresMut => {}
zbus::object_server::DispatchResult::NotFound => {
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 Expand Up @@ -124,7 +135,7 @@ impl Properties {
.instance
.read()
.await
.get_all(server, connection, &Some(header))
.get_all(server, connection, Some(&header))
.await?;
Ok(res)
}
Expand Down
21 changes: 16 additions & 5 deletions zbus/src/object_server/interface/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,15 +58,15 @@ pub trait Interface: Any + Send + Sync {
property_name: &str,
server: &ObjectServer,
connection: &Connection,
header: &Option<message::Header<'_>>,
header: Option<&message::Header<'_>>,
) -> Option<fdo::Result<OwnedValue>>;

/// Return all the properties.
async fn get_all(
&self,
object_server: &ObjectServer,
connection: &Connection,
header: &Option<message::Header<'_>>,
header: Option<&message::Header<'_>>,
) -> fdo::Result<HashMap<String, OwnedValue>>;

/// Set a property value.
Expand All @@ -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: &'call Option<message::Header<'_>>,
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,8 +103,10 @@ pub trait Interface: Any + Send + Sync {
&mut self,
property_name: &str,
value: &Value<'_>,
object_server: &ObjectServer,
connection: &Connection,
emitter: &SignalEmitter<'_>,
header: &Option<Header<'_>>,
header: Option<&Header<'_>>,
) -> Option<fdo::Result<()>>;

/// Call a method.
Expand Down
2 changes: 1 addition & 1 deletion zbus/src/object_server/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ impl Node {
.instance
.read()
.await
.get_all(object_server, connection, &None)
.get_all(object_server, connection, None)
.await
}
}
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
99 changes: 66 additions & 33 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 @@ -719,7 +734,7 @@ pub fn expand(args: Punctuated<Meta, Token![,]>, mut input: ItemImpl) -> syn::Re
&self,
signal_emitter: &#zbus::object_server::SignalEmitter<'_>,
) -> #zbus::Result<()> {
let header = None as Option<#zbus::message::Header<'_>>;
let header = ::std::option::Option::None::<&#zbus::message::Header<'_>>;
let connection = signal_emitter.connection();
let object_server = connection.object_server();
#args_from_msg
Expand Down Expand Up @@ -864,7 +879,7 @@ pub fn expand(args: Punctuated<Meta, Token![,]>, mut input: ItemImpl) -> syn::Re
property_name: &str,
object_server: &#zbus::ObjectServer,
connection: &#zbus::Connection,
header: &Option<#zbus::message::Header<'_>>,
header: Option<&#zbus::message::Header<'_>>,
) -> ::std::option::Option<#zbus::fdo::Result<#zbus::zvariant::OwnedValue>> {
match property_name {
#get_dispatch
Expand All @@ -876,7 +891,7 @@ pub fn expand(args: Punctuated<Meta, Token![,]>, mut input: ItemImpl) -> syn::Re
&self,
object_server: &#zbus::ObjectServer,
connection: &#zbus::Connection,
header: &Option<#zbus::message::Header<'_>>,
header: Option<&#zbus::message::Header<'_>>,
) -> #zbus::fdo::Result<::std::collections::HashMap<
::std::string::String,
#zbus::zvariant::OwnedValue,
Expand All @@ -893,8 +908,10 @@ 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: &'call Option<#zbus::message::Header<'_>>,
header: Option<&'call #zbus::message::Header<'_>>,
) -> #zbus::object_server::DispatchResult<'call> {
match property_name {
#set_dispatch
Expand All @@ -906,8 +923,10 @@ 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<'_>>,
header: Option<&#zbus::message::Header<'_>>,
) -> ::std::option::Option<#zbus::fdo::Result<()>> {
match property_name {
#set_mut_dispatch
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.clone(); }),
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 279b7a7

Please sign in to comment.