use crate::{Message, MessageType, Error, to_c_str, c_str_to_slice}; use std::ptr; use std::collections::HashMap; /// [Unstable and Experimental] pub trait MessageDispatcherConfig: Sized { /// The type of method reply stored inside the dispatcher type Reply; /// Called when a method call has received a reply. fn on_reply(reply: Self::Reply, msg: Message, dispatcher: &mut MessageDispatcher); /// Called when a signal is received. /// /// Defaults to doing nothing. #[allow(unused_variables)] fn on_signal(msg: Message, dispatcher: &mut MessageDispatcher) {} /// Called when a method call is received. /// /// Defaults to calling default_dispatch. fn on_method_call(msg: Message, dispatcher: &mut MessageDispatcher) { if let Some(reply) = MessageDispatcher::::default_dispatch(&msg) { Self::on_send(reply, dispatcher); } } /// Called in the other direction, i e, when a message should be sent over the connection. fn on_send(msg: Message, dispatcher: &mut MessageDispatcher); } /// Dummy implementation impl MessageDispatcherConfig for () { type Reply = (); fn on_reply(_: Self::Reply, _: Message, _: &mut MessageDispatcher) { unreachable!() } fn on_send(_: Message, _: &mut MessageDispatcher) { unreachable!() } } /// [Unstable and Experimental] Meant for usage with RxTx. pub struct MessageDispatcher { waiting_replies: HashMap, inner: C, } impl MessageDispatcher { /// Creates a new message dispatcher. pub fn new(inner: C) -> Self { MessageDispatcher { waiting_replies: HashMap::new(), inner: inner, } } /// "Inner" accessor pub fn inner(&self) -> &C { &self.inner } /// "Inner" mutable accessor pub fn inner_mut(&mut self) -> &mut C { &mut self.inner } /// Adds a waiting reply to a method call. func will be called when a method reply is dispatched. pub fn add_reply(&mut self, serial: u32, func: C::Reply) { if let Some(_) = self.waiting_replies.insert(serial, func) { // panic because we're overwriting something else, or just ignore? } } /// Cancels a waiting reply. pub fn cancel_reply(&mut self, serial: u32) -> Option { self.waiting_replies.remove(&serial) } /// Dispatch an incoming message. pub fn dispatch(&mut self, msg: Message) { if let Some(serial) = msg.get_reply_serial() { if let Some(sender) = self.waiting_replies.remove(&serial) { C::on_reply(sender, msg, self); return; } } match msg.msg_type() { MessageType::Signal => C::on_signal(msg, self), MessageType::MethodCall => C::on_method_call(msg, self), MessageType::Error | MessageType::MethodReturn => {}, MessageType::Invalid => unreachable!(), } } /// Handles what we need to be a good D-Bus citizen. /// /// Call this if you have not handled the message yourself: /// * It handles calls to org.freedesktop.DBus.Peer. /// * For other method calls, it sends an error reply back that the method was unknown. pub fn default_dispatch(m: &Message) -> Option { Self::peer(&m) .or_else(|| Self::unknown_method(&m)) } /// Replies if this is a call to org.freedesktop.DBus.Peer, otherwise returns None. pub fn peer(m: &Message) -> Option { if let Some(intf) = m.interface() { if &*intf != "org.freedesktop.DBus.Peer" { return None; } if let Some(method) = m.member() { if &*method == "Ping" { return Some(m.method_return()) } if &*method == "GetMachineId" { let mut r = m.method_return(); let mut e = Error::empty(); unsafe { let id = ffi::dbus_try_get_local_machine_id(e.get_mut()); if id != ptr::null_mut() { r = r.append1(c_str_to_slice(&(id as *const _)).unwrap()); ffi::dbus_free(id as *mut _); return Some(r) } } } } Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Method does not exist"))) } else { None } } /// For method calls, it replies that the method was unknown, otherwise returns None. pub fn unknown_method(m: &Message) -> Option { if m.msg_type() != MessageType::MethodCall { return None; } // if m.get_no_reply() { return None; } // The reference implementation does not do this? Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Path, Interface, or Method does not exist"))) } }