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