Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✨ zb,zm: Support Option<Header<'_>> parameter in property getters #1176

Merged
merged 3 commits into from
Dec 18, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions zbus/src/fdo/object_manager.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ use zbus_names::{InterfaceName, OwnedInterfaceName};
use zvariant::{ObjectPath, OwnedObjectPath, OwnedValue, Value};

use super::{Error, Result};
use crate::{interface, message::Header, object_server::SignalEmitter, ObjectServer};
use crate::{interface, message::Header, object_server::SignalEmitter, Connection, ObjectServer};

/// The type returned by the [`ObjectManagerProxy::get_managed_objects`] method.
pub type ManagedObjects =
@@ -49,6 +49,7 @@ impl ObjectManager {
async fn get_managed_objects(
&self,
#[zbus(object_server)] server: &ObjectServer,
#[zbus(connection)] connection: &Connection,
#[zbus(header)] header: Header<'_>,
) -> Result<ManagedObjects> {
let path = header.path().ok_or(crate::Error::MissingField)?;
@@ -57,7 +58,7 @@ impl ObjectManager {
.get_child(path)
.ok_or_else(|| Error::UnknownObject(format!("Unknown object '{path}'")))?;

node.get_managed_objects().await
node.get_managed_objects(server, connection).await
}

/// This signal is emitted when either a new object is added or when an existing object gains
32 changes: 22 additions & 10 deletions zbus/src/fdo/properties.rs
Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ use zbus_names::InterfaceName;
use zvariant::{OwnedValue, Value};

use super::{Error, Result};
use crate::{interface, message::Header, object_server::SignalEmitter, ObjectServer};
use crate::{interface, message::Header, object_server::SignalEmitter, Connection, ObjectServer};

/// Service-side implementation for the `org.freedesktop.DBus.Properties` interface.
/// This interface is implemented automatically for any object registered to the
@@ -29,6 +29,7 @@ impl Properties {
&self,
interface_name: InterfaceName<'_>,
property_name: &str,
#[zbus(connection)] conn: &Connection,
#[zbus(object_server)] server: &ObjectServer,
#[zbus(header)] header: Header<'_>,
) -> Result<OwnedValue> {
@@ -41,7 +42,12 @@ impl Properties {
Error::UnknownInterface(format!("Unknown interface '{interface_name}'"))
})?;

let res = iface.instance.read().await.get(property_name).await;
let res = iface
.instance
.read()
.await
.get(property_name, server, conn, &Some(header))
.await;
res.unwrap_or_else(|| {
Err(Error::UnknownProperty(format!(
"Unknown property '{property_name}'"
@@ -68,12 +74,12 @@ impl Properties {
Error::UnknownInterface(format!("Unknown interface '{interface_name}'"))
})?;

match iface
.instance
.read()
.await
.set(property_name, &value, &emitter)
{
match iface.instance.read().await.set(
property_name,
&value,
&emitter,
&Some(header.clone()),
) {
zbus::object_server::DispatchResult::RequiresMut => {}
zbus::object_server::DispatchResult::NotFound => {
return Err(Error::UnknownProperty(format!(
@@ -88,7 +94,7 @@ impl Properties {
.instance
.write()
.await
.set_mut(property_name, &value, &emitter)
.set_mut(property_name, &value, &emitter, &Some(header))
.await;
res.unwrap_or_else(|| {
Err(Error::UnknownProperty(format!(
@@ -102,6 +108,7 @@ impl Properties {
&self,
interface_name: InterfaceName<'_>,
#[zbus(object_server)] server: &ObjectServer,
#[zbus(connection)] connection: &Connection,
#[zbus(header)] header: Header<'_>,
) -> Result<HashMap<String, OwnedValue>> {
let path = header.path().ok_or(crate::Error::MissingField)?;
@@ -113,7 +120,12 @@ impl Properties {
Error::UnknownInterface(format!("Unknown interface '{interface_name}'"))
})?;

let res = iface.instance.read().await.get_all().await?;
let res = iface
.instance
.read()
.await
.get_all(server, connection, &Some(header))
.await?;
Ok(res)
}

31 changes: 26 additions & 5 deletions zbus/src/object_server/interface/mod.rs
Original file line number Diff line number Diff line change
@@ -17,8 +17,11 @@ use zbus_names::{InterfaceName, MemberName};
use zvariant::{OwnedValue, Value};

use crate::{
async_lock::RwLock, fdo, message::Message, object_server::SignalEmitter, Connection,
ObjectServer,
async_lock::RwLock,
fdo,
message::{self, Header, Message},
object_server::SignalEmitter,
Connection, ObjectServer,
};

/// This trait is used to dispatch messages to an interface instance.
@@ -45,10 +48,26 @@ pub trait Interface: Any + Send + Sync {
}

/// Get a property value. Returns `None` if the property doesn't exist.
async fn get(&self, property_name: &str) -> Option<fdo::Result<OwnedValue>>;
///
/// Note: The header parameter will be None when the getter is not being called as part
/// of D-Bus communication (for example, when it is called as part of initial object setup,
/// before it is registered on the bus, or when we manually send out property changed
/// notifications).
async fn get(
&self,
property_name: &str,
server: &ObjectServer,
connection: &Connection,
header: &Option<message::Header<'_>>,
) -> Option<fdo::Result<OwnedValue>>;

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

/// Set a property value.
///
@@ -60,8 +79,9 @@ pub trait Interface: Any + Send + Sync {
property_name: &'call str,
value: &'call Value<'_>,
emitter: &'call SignalEmitter<'_>,
header: &'call Option<message::Header<'_>>,
) -> DispatchResult<'call> {
let _ = (property_name, value, emitter);
let _ = (property_name, value, emitter, header);
DispatchResult::RequiresMut
}

@@ -75,6 +95,7 @@ pub trait Interface: Any + Send + Sync {
property_name: &str,
value: &Value<'_>,
emitter: &SignalEmitter<'_>,
header: &Option<Header<'_>>,
) -> Option<fdo::Result<()>>;

/// Call a method.
6 changes: 4 additions & 2 deletions zbus/src/object_server/mod.rs
Original file line number Diff line number Diff line change
@@ -148,7 +148,7 @@ impl ObjectServer {
if name == ObjectManager::name() {
// Just added an object manager. Need to signal all managed objects under it.
let ctxt = SignalEmitter::new(&self.connection(), path)?;
let objects = node.get_managed_objects().await?;
let objects = node.get_managed_objects(self, &self.connection()).await?;
for (path, owned_interfaces) in objects {
let interfaces = owned_interfaces
.iter()
@@ -165,7 +165,9 @@ impl ObjectServer {
} else if let Some(manager_path) = manager_path {
let ctxt = SignalEmitter::new(&self.connection(), manager_path.clone())?;
let mut interfaces = HashMap::new();
let owned_props = node.get_properties(name.clone()).await?;
let owned_props = node
.get_properties(self, &self.connection(), name.clone())
.await?;
let props = owned_props
.iter()
.map(|(k, v)| Ok((k.as_str(), Value::try_from(v)?)))
19 changes: 15 additions & 4 deletions zbus/src/object_server/node.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,10 @@ use std::{
use zbus_names::InterfaceName;
use zvariant::{ObjectPath, OwnedObjectPath, OwnedValue};

use crate::fdo::{self, Introspectable, ManagedObjects, ObjectManager, Peer, Properties};
use crate::{
fdo::{self, Introspectable, ManagedObjects, ObjectManager, Peer, Properties},
Connection, ObjectServer,
};

use super::{ArcInterface, Interface};

@@ -208,7 +211,11 @@ impl Node {
xml
}

pub(crate) async fn get_managed_objects(&self) -> fdo::Result<ManagedObjects> {
pub(crate) async fn get_managed_objects(
&self,
object_server: &ObjectServer,
connection: &Connection,
) -> fdo::Result<ManagedObjects> {
let mut managed_objects = ManagedObjects::new();

// Recursively get all properties of all interfaces of descendants.
@@ -222,7 +229,9 @@ impl Node {
&& *n != &Properties::name()
&& *n != &ObjectManager::name()
}) {
let props = node.get_properties(iface_name.clone()).await?;
let props = node
.get_properties(object_server, connection, iface_name.clone())
.await?;
interfaces.insert(iface_name.clone().into(), props);
}
managed_objects.insert(node.path.clone(), interfaces);
@@ -234,14 +243,16 @@ impl Node {

pub(super) async fn get_properties(
&self,
object_server: &ObjectServer,
connection: &Connection,
interface_name: InterfaceName<'_>,
) -> fdo::Result<HashMap<String, OwnedValue>> {
self.interface_lock(interface_name)
.expect("Interface was added but not found")
.instance
.read()
.await
.get_all()
.get_all(object_server, connection, &None)
.await
}
}
16 changes: 16 additions & 0 deletions zbus/tests/e2e.rs
Original file line number Diff line number Diff line change
@@ -262,6 +262,21 @@ impl MyIface {
self.count
}

#[instrument]
#[zbus(property)]
fn test_header_prop(
&self,
#[zbus(header)] header: Option<Header<'_>>,
#[zbus(connection)] connection: &Connection,
#[zbus(object_server)] object_server: &ObjectServer,
) -> bool {
debug!(
"`TestHeaderProp` getter called, header: {:?}, connection: {:?}, object_server: {:?}",
header, connection, object_server
);
header.is_some()
}

#[instrument]
#[zbus(property)]
async fn hash_map(&self) -> HashMap<String, String> {
@@ -570,6 +585,7 @@ async fn my_iface_test(conn: Connection, event: Event) -> zbus::Result<u32> {
drop(props_changed_stream);

proxy.ping().await?;
assert_eq!(proxy.test_header_prop().await?, true);
assert_eq!(proxy.count().await?, 1);
assert_eq!(proxy.cached_count()?, None);

218 changes: 128 additions & 90 deletions zbus_macros/src/iface.rs

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions zbus_macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -229,7 +229,7 @@ pub fn proxy(attr: TokenStream, item: TokenStream) -> TokenStream {
///
/// * `proxy` - If specified, a proxy type will also be generated for the interface. This attribute
/// supports all the [`macro@proxy`]-specific sub-attributes (e.g `gen_async`). The common
/// sub-attributes (e.g `name`) are automatically forworded to the [`macro@proxy`] macro.
/// sub-attributes (e.g `name`) are automatically forwarded to the [`macro@proxy`] macro.
///
/// * `introspection_docs` - whether to include the documentation in the introspection data
/// (Default: `true`). If your interface is well-known or well-documented, you may want to set
@@ -289,16 +289,20 @@ 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 arguments support the following `zbus` attributes:
/// The method and property getter 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.
/// 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.
/// * `signal_emitter` - This marks the method argument to receive a [`SignalEmitter`] instance,
/// which is needed for emitting signals the easy way.
/// which is needed for emitting signals the easy way. This argument is not available for property
/// getters.
///
/// # Example
///