use crate::{Message, MessageType, Error, to_c_str, c_str_to_slice};
use std::ptr;
use std::collections::HashMap;
pub trait MessageDispatcherConfig: Sized {
type Reply;
fn on_reply(reply: Self::Reply, msg: Message, dispatcher: &mut MessageDispatcher<Self>);
#[allow(unused_variables)]
fn on_signal(msg: Message, dispatcher: &mut MessageDispatcher<Self>) {}
fn on_method_call(msg: Message, dispatcher: &mut MessageDispatcher<Self>) {
if let Some(reply) = MessageDispatcher::<Self>::default_dispatch(&msg) {
Self::on_send(reply, dispatcher);
}
}
fn on_send(msg: Message, dispatcher: &mut MessageDispatcher<Self>);
}
impl MessageDispatcherConfig for () {
type Reply = ();
fn on_reply(_: Self::Reply, _: Message, _: &mut MessageDispatcher<Self>) { unreachable!() }
fn on_send(_: Message, _: &mut MessageDispatcher<Self>) { unreachable!() }
}
pub struct MessageDispatcher<C: MessageDispatcherConfig> {
waiting_replies: HashMap<u32, C::Reply>,
inner: C,
}
impl<C: MessageDispatcherConfig> MessageDispatcher<C> {
pub fn new(inner: C) -> Self { MessageDispatcher {
waiting_replies: HashMap::new(),
inner: inner,
} }
pub fn inner(&self) -> &C { &self.inner }
pub fn inner_mut(&mut self) -> &mut C { &mut self.inner }
pub fn add_reply(&mut self, serial: u32, func: C::Reply) {
if let Some(_) = self.waiting_replies.insert(serial, func) {
}
}
pub fn cancel_reply(&mut self, serial: u32) -> Option<C::Reply> {
self.waiting_replies.remove(&serial)
}
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 => {},
}
}
pub fn default_dispatch(m: &Message) -> Option<Message> {
Self::peer(&m)
.or_else(|| Self::unknown_method(&m))
}
pub fn peer(m: &Message) -> Option<Message> {
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 }
}
pub fn unknown_method(m: &Message) -> Option<Message> {
if m.msg_type() != MessageType::MethodCall { return None; }
Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Path, Interface, or Method does not exist")))
}
}