1 | use std::{ |
2 | any::{Any, TypeId}, |
3 | collections::HashMap, |
4 | fmt::Write, |
5 | future::Future, |
6 | pin::Pin, |
7 | }; |
8 | |
9 | use async_trait::async_trait; |
10 | use zbus::MessageFlags; |
11 | use zbus_names::{InterfaceName, MemberName}; |
12 | use zvariant::{DynamicType, OwnedValue, Value}; |
13 | |
14 | use crate::{fdo, Connection, Message, ObjectServer, Result, SignalContext}; |
15 | use tracing::trace; |
16 | |
17 | /// A helper type returned by [`Interface`] callbacks. |
18 | pub 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 | |
31 | impl<'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 ] |
67 | pub 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. |
142 | impl 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 | } |
163 | |