zbus/
interface.rs

1use std::{
2    any::{Any, TypeId},
3    collections::HashMap,
4    fmt::Write,
5    future::Future,
6    pin::Pin,
7};
8
9use async_trait::async_trait;
10use zbus::MessageFlags;
11use zbus_names::{InterfaceName, MemberName};
12use zvariant::{DynamicType, OwnedValue, Value};
13
14use crate::{fdo, Connection, Message, ObjectServer, Result, SignalContext};
15use tracing::trace;
16
17/// A helper type returned by [`Interface`] callbacks.
18pub enum DispatchResult<'a> {
19    /// This interface does not support the given method
20    NotFound,
21
22    /// Retry with [Interface::call_mut].
23    ///
24    /// This is equivalent to NotFound if returned by call_mut.
25    RequiresMut,
26
27    /// The method was found and will be completed by running this Future
28    Async(Pin<Box<dyn Future<Output = Result<()>> + Send + 'a>>),
29}
30
31impl<'a> DispatchResult<'a> {
32    /// Helper for creating the Async variant
33    pub fn new_async<F, T, E>(conn: &'a Connection, msg: &'a Message, f: F) -> Self
34    where
35        F: Future<Output = ::std::result::Result<T, E>> + Send + 'a,
36        T: serde::Serialize + DynamicType + Send + Sync,
37        E: zbus::DBusError + Send,
38    {
39        DispatchResult::Async(Box::pin(async move {
40            let hdr = msg.header()?;
41            let ret = f.await;
42            if !hdr
43                .primary()
44                .flags()
45                .contains(MessageFlags::NoReplyExpected)
46            {
47                match ret {
48                    Ok(r) => conn.reply(msg, &r).await,
49                    Err(e) => conn.reply_dbus_error(&hdr, e).await,
50                }
51                .map(|_seq| ())
52            } else {
53                trace!("No reply expected for {:?} by the caller.", msg);
54                Ok(())
55            }
56        }))
57    }
58}
59
60/// The trait used to dispatch messages to an interface instance.
61///
62/// Note: It is not recommended to manually implement this trait. The [`dbus_interface`] macro
63/// implements it for you.
64///
65/// [`dbus_interface`]: attr.dbus_interface.html
66#[async_trait]
67pub trait Interface: Any + Send + Sync {
68    /// Return the name of the interface. Ex: "org.foo.MyInterface"
69    fn name() -> InterfaceName<'static>
70    where
71        Self: Sized;
72
73    /// Get a property value. Returns `None` if the property doesn't exist.
74    async fn get(&self, property_name: &str) -> Option<fdo::Result<OwnedValue>>;
75
76    /// Return all the properties.
77    async fn get_all(&self) -> HashMap<String, OwnedValue>;
78
79    /// Set a property value.
80    ///
81    /// Return [`DispatchResult::NotFound`] if the property doesn't exist, or
82    /// [`DispatchResult::RequiresMut`] if `set_mut` should be used instead.  The default
83    /// implementation just returns `RequiresMut`.
84    fn set<'call>(
85        &'call self,
86        property_name: &'call str,
87        value: &'call Value<'_>,
88        ctxt: &'call SignalContext<'_>,
89    ) -> DispatchResult<'call> {
90        let _ = (property_name, value, ctxt);
91        DispatchResult::RequiresMut
92    }
93
94    /// Set a property value.
95    ///
96    /// Returns `None` if the property doesn't exist.
97    ///
98    /// This will only be invoked if `set` returned `RequiresMut`.
99    async fn set_mut(
100        &mut self,
101        property_name: &str,
102        value: &Value<'_>,
103        ctxt: &SignalContext<'_>,
104    ) -> Option<fdo::Result<()>>;
105
106    /// Call a method.
107    ///
108    /// Return [`DispatchResult::NotFound`] if the method doesn't exist, or
109    /// [`DispatchResult::RequiresMut`] if `call_mut` should be used instead.
110    ///
111    /// It is valid, though inefficient, for this to always return `RequiresMut`.
112    fn call<'call>(
113        &'call self,
114        server: &'call ObjectServer,
115        connection: &'call Connection,
116        msg: &'call Message,
117        name: MemberName<'call>,
118    ) -> DispatchResult<'call>;
119
120    /// Call a `&mut self` method.
121    ///
122    /// This will only be invoked if `call` returned `RequiresMut`.
123    fn call_mut<'call>(
124        &'call mut self,
125        server: &'call ObjectServer,
126        connection: &'call Connection,
127        msg: &'call Message,
128        name: MemberName<'call>,
129    ) -> DispatchResult<'call>;
130
131    /// Write introspection XML to the writer, with the given indentation level.
132    fn introspect_to_writer(&self, writer: &mut dyn Write, level: usize);
133}
134
135// Note: while it is possible to implement this without `unsafe`, it currently requires a helper
136// trait with a blanket impl that creates `dyn Any` refs.  It's simpler (and more performant) to
137// just check the type ID and do the downcast ourself.
138//
139// See https://github.com/rust-lang/rust/issues/65991 for a rustc feature that will make it
140// possible to get a `dyn Any` ref directly from a `dyn Interface` ref; once that is stable, we can
141// remove this unsafe code.
142impl dyn Interface {
143    /// Return Any of self
144    pub(crate) fn downcast_ref<T: Any>(&self) -> Option<&T> {
145        if <dyn Interface as Any>::type_id(self) == TypeId::of::<T>() {
146            // SAFETY: If type ID matches, it means object is of type T
147            Some(unsafe { &*(self as *const dyn Interface as *const T) })
148        } else {
149            None
150        }
151    }
152
153    /// Return Any of self
154    pub(crate) fn downcast_mut<T: Any>(&mut self) -> Option<&mut T> {
155        if <dyn Interface as Any>::type_id(self) == TypeId::of::<T>() {
156            // SAFETY: If type ID matches, it means object is of type T
157            Some(unsafe { &mut *(self as *mut dyn Interface as *mut T) })
158        } else {
159            None
160        }
161    }
162}