zbus/fdo/
properties.rs

1//! D-Bus standard interfaces.
2//!
3//! The D-Bus specification defines the message bus messages and some standard interfaces that may
4//! be useful across various D-Bus applications. This module provides their proxy.
5
6use std::{borrow::Cow, collections::HashMap};
7use zbus_names::InterfaceName;
8use zvariant::{OwnedValue, Value};
9
10use super::{Error, Result};
11use crate::{interface, message::Header, object_server::SignalEmitter, Connection, ObjectServer};
12
13/// Service-side implementation for the `org.freedesktop.DBus.Properties` interface.
14/// This interface is implemented automatically for any object registered to the
15/// [ObjectServer].
16pub struct Properties;
17
18#[interface(
19    name = "org.freedesktop.DBus.Properties",
20    introspection_docs = false,
21    proxy(visibility = "pub")
22)]
23impl Properties {
24    /// Get a property value.
25    async fn get(
26        &self,
27        interface_name: InterfaceName<'_>,
28        property_name: &str,
29        #[zbus(connection)] conn: &Connection,
30        #[zbus(object_server)] server: &ObjectServer,
31        #[zbus(header)] header: Header<'_>,
32        #[zbus(signal_emitter)] emitter: SignalEmitter<'_>,
33    ) -> Result<OwnedValue> {
34        let path = header.path().ok_or(crate::Error::MissingField)?;
35        let root = server.root().read().await;
36        let iface = root
37            .get_child(path)
38            .and_then(|node| node.interface_lock(interface_name.as_ref()))
39            .ok_or_else(|| {
40                Error::UnknownInterface(format!("Unknown interface '{interface_name}'"))
41            })?;
42
43        let res = iface
44            .instance
45            .read()
46            .await
47            .get(property_name, server, conn, Some(&header), &emitter)
48            .await;
49        res.unwrap_or_else(|| {
50            Err(Error::UnknownProperty(format!(
51                "Unknown property '{property_name}'"
52            )))
53        })
54    }
55
56    /// Set a property value.
57    #[allow(clippy::too_many_arguments)]
58    async fn set(
59        &self,
60        interface_name: InterfaceName<'_>,
61        property_name: &str,
62        value: Value<'_>,
63        #[zbus(object_server)] server: &ObjectServer,
64        #[zbus(connection)] connection: &Connection,
65        #[zbus(header)] header: Header<'_>,
66        #[zbus(signal_emitter)] emitter: SignalEmitter<'_>,
67    ) -> Result<()> {
68        let path = header.path().ok_or(crate::Error::MissingField)?;
69        let root = server.root().read().await;
70        let iface = root
71            .get_child(path)
72            .and_then(|node| node.interface_lock(interface_name.as_ref()))
73            .ok_or_else(|| {
74                Error::UnknownInterface(format!("Unknown interface '{interface_name}'"))
75            })?;
76
77        match iface.instance.read().await.set(
78            property_name,
79            &value,
80            server,
81            connection,
82            Some(&header),
83            &emitter,
84        ) {
85            zbus::object_server::DispatchResult::RequiresMut => {}
86            zbus::object_server::DispatchResult::NotFound => {
87                return Err(Error::UnknownProperty(format!(
88                    "Unknown property '{property_name}'"
89                )));
90            }
91            zbus::object_server::DispatchResult::Async(f) => {
92                return f.await.map_err(Into::into);
93            }
94        }
95        let res = iface
96            .instance
97            .write()
98            .await
99            .set_mut(
100                property_name,
101                &value,
102                server,
103                connection,
104                Some(&header),
105                &emitter,
106            )
107            .await;
108        res.unwrap_or_else(|| {
109            Err(Error::UnknownProperty(format!(
110                "Unknown property '{property_name}'"
111            )))
112        })
113    }
114
115    /// Get all properties.
116    async fn get_all(
117        &self,
118        interface_name: InterfaceName<'_>,
119        #[zbus(object_server)] server: &ObjectServer,
120        #[zbus(connection)] connection: &Connection,
121        #[zbus(header)] header: Header<'_>,
122        #[zbus(signal_emitter)] emitter: SignalEmitter<'_>,
123    ) -> Result<HashMap<String, OwnedValue>> {
124        let path = header.path().ok_or(crate::Error::MissingField)?;
125        let root = server.root().read().await;
126        let iface = root
127            .get_child(path)
128            .and_then(|node| node.interface_lock(interface_name.as_ref()))
129            .ok_or_else(|| {
130                Error::UnknownInterface(format!("Unknown interface '{interface_name}'"))
131            })?;
132
133        let res = iface
134            .instance
135            .read()
136            .await
137            .get_all(server, connection, Some(&header), &emitter)
138            .await?;
139        Ok(res)
140    }
141
142    /// Emit the `org.freedesktop.DBus.Properties.PropertiesChanged` signal.
143    #[zbus(signal)]
144    #[rustfmt::skip]
145    pub async fn properties_changed(
146        emitter: &SignalEmitter<'_>,
147        interface_name: InterfaceName<'_>,
148        changed_properties: HashMap<&str, Value<'_>>,
149        invalidated_properties: Cow<'_, [&str]>,
150    ) -> zbus::Result<()>;
151}