use std::marker::PhantomData;
use dbus::arg::AppendAll;
use dbus::channel::Sender;
use std::sync::Arc;
use crate::{MethodErr, utils::Dbg};
#[derive(Debug)]
pub struct Context {
path: dbus::Path<'static>,
interface: Option<dbus::strings::Interface<'static>>,
method: dbus::strings::Member<'static>,
message: dbus::Message,
has_error: bool,
reply: Option<dbus::Message>,
send_extra: Vec<dbus::Message>,
send_on_drop: Option<Dbg<Arc<dyn Sender + Send + Sync>>>,
}
impl Context {
pub fn new(msg: dbus::Message) -> Option<Self> {
if msg.msg_type() != dbus::MessageType::MethodCall { return None; }
let p = msg.path()?.into_static();
let i = msg.interface().map(|i| i.into_static());
let m = msg.member()?.into_static();
Some(Context {
path: p,
interface: i,
method: m,
message: msg,
reply: None,
send_on_drop: None,
send_extra: vec!(),
has_error: false,
})
}
pub fn check<R, F: FnOnce(&mut Context) -> Result<R, MethodErr>>(&mut self, f: F) -> Result<R, ()> {
f(self).map_err(|e| { self.reply_err(e); })
}
pub fn do_reply<F: FnOnce(&mut dbus::Message)>(&mut self, f: F) {
if self.message.get_no_reply() { return; }
if self.reply.is_some() { return; }
let mut msg = self.message.method_return();
f(&mut msg);
self.reply = Some(msg);
}
pub (crate) fn reply_ok<OA: AppendAll>(&mut self, oa: OA) -> PhantomData<OA> {
self.do_reply(|msg| { msg.append_all(oa); });
PhantomData
}
pub fn reply<OA: AppendAll>(&mut self, result: Result<OA, MethodErr>) -> PhantomData<OA> {
match result {
Ok(oa) => { self.reply_ok(oa); },
Err(e) => { self.reply_err(e); },
};
PhantomData
}
pub (crate) fn reply_err(&mut self, err: MethodErr) {
self.has_error = true;
if !self.message.get_no_reply() {
self.reply = Some(err.to_message(&self.message))
};
}
pub fn set_reply(&mut self, msg: Option<dbus::Message>, check_no_reply: bool, check_set: bool) {
if check_no_reply && self.message.get_no_reply() { return; }
if check_set && self.reply.is_some() { return; }
self.reply = msg;
}
pub fn flush_messages<S: dbus::channel::Sender + ?Sized>(&mut self, conn: &S) -> Result<(), ()> {
if let Some(msg) = self.reply.take() {
conn.send(msg)?;
}
for msg in self.send_extra.drain(..) {
conn.send(msg)?;
}
Ok(())
}
pub fn make_signal<'b, A, N>(&self, name: N, args: A) -> dbus::Message
where A: dbus::arg::AppendAll, N: Into<dbus::strings::Member<'b>> {
let mut msg = dbus::Message::signal(&self.path, self.interface.as_ref().unwrap(), &name.into());
msg.append_all(args);
msg
}
pub fn push_msg(&mut self, msg: dbus::Message) { self.send_extra.push(msg); }
pub fn path(&self) -> &dbus::Path<'static> { &self.path }
pub fn interface(&self) -> Option<&dbus::strings::Interface<'static>> { self.interface.as_ref() }
pub fn method(&self) -> &dbus::strings::Member<'static> { &self.method }
pub fn message(&self) -> &dbus::Message { &self.message }
pub fn has_reply(&self) -> bool { self.reply.is_some() }
pub fn has_error(&self) -> bool { self.has_error }
pub (crate) fn set_send_on_drop(&mut self, value: Arc<dyn Sender + Send + Sync>) {
self.send_on_drop = Some(Dbg(value));
}
}
impl Drop for Context {
fn drop(&mut self) {
if let Some(sender) = self.send_on_drop.take() {
let _ = self.flush_messages(&*sender.0);
}
}
}