use super::{Error, ffi, Message, MessageType};
use crate::strings::{BusName, Path, Member, Interface};
use crate::arg::{AppendAll, ReadAll, IterAppend};
use crate::message::SignalArgs;
pub mod stdintf;
mod connection;
pub use connection::{Connection, ConnMsgs};
#[derive(Clone, Debug)]
pub struct ConnPath<'a, C> {
pub conn: C,
pub dest: BusName<'a>,
pub path: Path<'a>,
pub timeout: i32,
}
impl<'a, C: ::std::ops::Deref<Target=Connection>> ConnPath<'a, C> {
pub fn method_call_with_args<F: FnOnce(&mut Message)>(&self, i: &Interface, m: &Member, f: F) -> Result<Message, Error> {
let mut msg = Message::method_call(&self.dest, &self.path, i, m);
f(&mut msg);
self.conn.send_with_reply_and_block(msg, self.timeout)
}
pub fn signal_with_args<F: FnOnce(&mut Message)>(&self, i: &Interface, m: &Member, f: F) -> Result<u32, Error> {
let mut msg = Message::signal(&self.path, i, m);
f(&mut msg);
self.conn.send(msg).map_err(|_| Error::new_failed("Sending signal failed"))
}
pub fn emit<S: SignalArgs + AppendAll>(&self, signal: &S) -> Result<u32, Error> {
let msg = signal.to_emit_message(&self.path);
self.conn.send(msg).map_err(|_| Error::new_failed("Sending signal failed"))
}
pub fn method_call<'i, 'm, R: ReadAll, A: AppendAll, I: Into<Interface<'i>>, M: Into<Member<'m>>>(&self, i: I, m: M, args: A) -> Result<R, Error> {
let mut r = self.method_call_with_args(&i.into(), &m.into(), |mut msg| {
args.append(&mut IterAppend::new(&mut msg));
})?;
r.as_result()?;
Ok(R::read(&mut r.iter_init())?)
}
}
pub type MessageCallback = Box<dyn FnMut(&Connection, Message) -> bool + 'static>;
pub use crate::ffi::DBusRequestNameReply as RequestNameReply;
pub use crate::ffi::DBusReleaseNameReply as ReleaseNameReply;
pub use crate::ffi::DBusBusType as BusType;
mod watch;
pub use self::watch::{Watch, WatchEvent};
use watch::WatchList;
#[repr(C)]
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub enum NameFlag {
AllowReplacement = ffi::DBUS_NAME_FLAG_ALLOW_REPLACEMENT as isize,
ReplaceExisting = ffi::DBUS_NAME_FLAG_REPLACE_EXISTING as isize,
DoNotQueue = ffi::DBUS_NAME_FLAG_DO_NOT_QUEUE as isize,
}
impl NameFlag {
pub fn value(self) -> u32 { self as u32 }
}
#[derive(Debug)]
pub enum ConnectionItem {
Nothing,
MethodCall(Message),
Signal(Message),
MethodReturn(Message),
}
impl From<Message> for ConnectionItem {
fn from(m: Message) -> Self {
let mtype = m.msg_type();
match mtype {
MessageType::Signal => ConnectionItem::Signal(m),
MessageType::MethodReturn => ConnectionItem::MethodReturn(m),
MessageType::Error => ConnectionItem::MethodReturn(m),
MessageType::MethodCall => ConnectionItem::MethodCall(m),
}
}
}
#[derive(Clone, Debug)]
pub enum MsgHandlerType {
All,
MsgType(MessageType),
Reply(u32),
}
impl MsgHandlerType {
fn matches_msg(&self, m: &Message) -> bool {
match *self {
MsgHandlerType::All => true,
MsgHandlerType::MsgType(t) => m.msg_type() == t,
MsgHandlerType::Reply(serial) => {
let t = m.msg_type();
((t == MessageType::MethodReturn) || (t == MessageType::Error)) && (m.get_reply_serial() == Some(serial))
}
}
}
}
pub trait MsgHandler {
fn handler_type(&self) -> MsgHandlerType;
fn handle_msg(&mut self, _msg: &Message) -> Option<MsgHandlerResult> { None }
}
#[derive(Debug, Default)]
pub struct MsgHandlerResult {
pub handled: bool,
pub done: bool,
pub reply: Vec<Message>,
}
type MsgHandlerList = Vec<Box<dyn MsgHandler>>;
pub struct MessageReply<F>(Option<F>, u32);
impl<'a, F: FnOnce(Result<&Message, Error>) + 'a> MsgHandler for MessageReply<F> {
fn handler_type(&self) -> MsgHandlerType { MsgHandlerType::Reply(self.1) }
fn handle_msg(&mut self, msg: &Message) -> Option<MsgHandlerResult> {
let e = match msg.msg_type() {
MessageType::MethodReturn => Ok(msg),
MessageType::Error => Err(msg.set_error_from_msg().unwrap_err()),
_ => unreachable!(),
};
debug_assert_eq!(msg.get_reply_serial(), Some(self.1));
self.0.take().unwrap()(e);
return Some(MsgHandlerResult { handled: true, done: true, reply: Vec::new() })
}
}
#[cfg(test)]
mod test {
use super::{Connection, BusType, ConnectionItem, NameFlag,
RequestNameReply, ReleaseNameReply};
use crate::Message;
#[test]
fn connection() {
let c = Connection::get_private(BusType::Session).unwrap();
let n = c.unique_name();
assert!(n.starts_with(":1."));
println!("Connected to DBus, unique name: {}", n);
}
#[test]
fn invalid_message() {
let c = Connection::get_private(BusType::Session).unwrap();
let m = Message::new_method_call("foo.bar", "/", "foo.bar", "FooBar").unwrap();
let e = c.send_with_reply_and_block(m, 2000).err().unwrap();
assert!(e.name().unwrap() == "org.freedesktop.DBus.Error.ServiceUnknown");
}
#[test]
fn object_path() {
use std::sync::mpsc;
let (tx, rx) = mpsc::channel();
let thread = ::std::thread::spawn(move || {
let c = Connection::get_private(BusType::Session).unwrap();
c.register_object_path("/hello").unwrap();
tx.send(c.unique_name()).unwrap();
for n in c.iter(1000) {
match n {
ConnectionItem::MethodCall(ref m) => {
let reply = Message::new_method_return(m).unwrap();
c.send(reply).unwrap();
break;
}
_ => {}
}
}
c.unregister_object_path("/hello");
});
let c = Connection::get_private(BusType::Session).unwrap();
let n = rx.recv().unwrap();
let m = Message::new_method_call(&n, "/hello", "com.example.hello", "Hello").unwrap();
println!("Sending...");
let r = c.send_with_reply_and_block(m, 8000).unwrap();
let reply = r.get_items();
println!("{:?}", reply);
thread.join().unwrap();
}
#[test]
fn register_name() {
let c = Connection::get_private(BusType::Session).unwrap();
let n = format!("com.example.hello.test.register_name");
assert_eq!(c.register_name(&n, NameFlag::ReplaceExisting as u32).unwrap(), RequestNameReply::PrimaryOwner);
assert_eq!(c.release_name(&n).unwrap(), ReleaseNameReply::Released);
}
#[test]
fn signal() {
let c = Connection::get_private(BusType::Session).unwrap();
let iface = "com.example.signaltest";
let mstr = format!("interface='{}',member='ThisIsASignal'", iface);
c.add_match(&mstr).unwrap();
let m = Message::new_signal("/mysignal", iface, "ThisIsASignal").unwrap();
let uname = c.unique_name();
c.send(m).unwrap();
for n in c.iter(1000) {
match n {
ConnectionItem::Signal(s) => {
let (p, i, m) = (s.path(), s.interface(), s.member());
match (&*p.unwrap(), &*i.unwrap(), &*m.unwrap()) {
("/mysignal", "com.example.signaltest", "ThisIsASignal") => {
assert_eq!(&*s.sender().unwrap(), &*uname);
break;
},
(_, _, _) => println!("Other signal: {:?}", s),
}
}
_ => {},
}
}
c.remove_match(&mstr).unwrap();
}
#[test]
fn watch() {
let c = Connection::get_private(BusType::Session).unwrap();
let d = c.watch_fds();
assert!(d.len() > 0);
println!("Fds to watch: {:?}", d);
}
}