use crate::{Error, Message};
use crate::channel::{MatchingReceiver, Channel, Sender, Token};
use crate::strings::{BusName, Path, Interface, Member};
use crate::arg::{AppendAll, ReadAll, IterAppend};
use crate::message::MatchRule;
use std::sync::{Arc, Mutex};
use std::sync::atomic::{AtomicUsize, Ordering::SeqCst};
use std::{task, pin, mem};
use std::cell::RefCell;
use std::time::Duration;
use crate::filters::Filters;
use std::future::Future;
use std::time::Instant;
use std::collections::HashMap;
#[allow(missing_docs)]
mod generated_org_freedesktop_standard_interfaces;
mod generated_org_freedesktop_dbus;
pub mod stdintf {
#[allow(missing_docs)]
pub mod org_freedesktop_dbus {
pub use super::super::generated_org_freedesktop_standard_interfaces::*;
#[allow(unused_imports)]
pub(crate) use super::super::generated_org_freedesktop_dbus::*;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum RequestNameReply {
PrimaryOwner = 1,
InQueue = 2,
Exists = 3,
AlreadyOwner = 4,
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum ReleaseNameReply {
Released = 1,
NonExistent = 2,
NotOwner = 3,
}
}
}
type Replies<F> = HashMap<Token, F>;
pub struct LocalConnection {
channel: Channel,
filters: RefCell<Filters<LocalFilterCb>>,
replies: RefCell<Replies<LocalRepliesCb>>,
timeout_maker: Option<TimeoutMakerCb>,
waker: Option<WakerCb>,
}
pub struct Connection {
channel: Channel,
filters: RefCell<Filters<FilterCb>>,
replies: RefCell<Replies<RepliesCb>>,
timeout_maker: Option<TimeoutMakerCb>,
waker: Option<WakerCb>,
}
pub struct SyncConnection {
channel: Channel,
filters: Mutex<Filters<SyncFilterCb>>,
replies: Mutex<Replies<SyncRepliesCb>>,
timeout_maker: Option<TimeoutMakerCb>,
waker: Option<WakerCb>,
}
use stdintf::org_freedesktop_dbus::DBus;
macro_rules! connimpl {
($c: ident, $cb: ident, $rcb: ident $(, $ss:tt)*) => {
type
$cb = Box<dyn FnMut(Message, &$c) -> bool $(+ $ss)* + 'static>;
type
$rcb = Box<dyn FnOnce(Message, &$c) $(+ $ss)* + 'static>;
impl From<Channel> for $c {
fn from(x: Channel) -> Self {
$c {
channel: x,
replies: Default::default(),
filters: Default::default(),
timeout_maker: None,
waker: None,
}
}
}
impl AsRef<Channel> for $c {
fn as_ref(&self) -> &Channel { &self.channel }
}
impl Sender for $c {
fn send(&self, msg: Message) -> Result<u32, ()> { self.channel.send(msg) }
}
impl MatchingReceiver for $c {
type F = $cb;
fn start_receive(&self, m: MatchRule<'static>, f: Self::F) -> Token {
self.filters_mut().add(m, f)
}
fn stop_receive(&self, id: Token) -> Option<(MatchRule<'static>, Self::F)> {
self.filters_mut().remove(id)
}
}
impl NonblockReply for $c {
type F = $rcb;
fn send_with_reply(&self, msg: Message, f: Self::F) -> Result<Token, ()> {
let token = {
let mut replies = self.replies_mut();
self.channel.send(msg).map(|x| {
let t = Token(x as usize);
replies.insert(t, f);
t
})
};
if self.channel.has_messages_to_send() {
if self.waker.as_ref().map(|wake| wake().is_err() ).unwrap_or(false) {
return Err(());
}
}
token
}
fn cancel_reply(&self, id: Token) -> Option<Self::F> { self.replies_mut().remove(&id) }
fn make_f<G: FnOnce(Message, &Self) + Send + 'static>(g: G) -> Self::F { Box::new(g) }
fn timeout_maker(&self) -> Option<TimeoutMakerCb> { self.timeout_maker }
fn set_timeout_maker(&mut self, f: Option<TimeoutMakerCb>) -> Option<TimeoutMakerCb> {
mem::replace(&mut self.timeout_maker, f)
}
fn set_waker(&mut self, f: Option<WakerCb>) -> Option<WakerCb> {
mem::replace(&mut self.waker, f)
}
}
impl Process for $c {
fn process_one(&self, msg: Message) {
if let Some(serial) = msg.get_reply_serial() {
if let Some(f) = self.replies_mut().remove(&Token(serial as usize)) {
f(msg, self);
return;
}
}
let ff = self.filters_mut().remove_matching(&msg);
if let Some(mut ff) = ff {
if ff.2(msg, self) {
self.filters_mut().insert(ff);
}
} else if let Some(reply) = crate::channel::default_reply(&msg) {
let _ = self.send(reply);
}
}
}
impl $c {
fn dbus_proxy(&self) -> Proxy<&Self> {
Proxy::new("org.freedesktop.DBus", "/org/freedesktop/DBus", Duration::from_secs(10), self)
}
pub fn unique_name(&self) -> BusName { self.channel.unique_name().unwrap().into() }
pub async fn request_name<'a, N: Into<BusName<'a>>>(&self, name: N, allow_replacement: bool, replace_existing: bool, do_not_queue: bool)
-> Result<stdintf::org_freedesktop_dbus::RequestNameReply, Error> {
let flags: u32 =
if allow_replacement { 1 } else { 0 } +
if replace_existing { 2 } else { 0 } +
if do_not_queue { 4 } else { 0 };
let r = self.dbus_proxy().request_name(&name.into(), flags).await?;
use stdintf::org_freedesktop_dbus::RequestNameReply::*;
let all = [PrimaryOwner, InQueue, Exists, AlreadyOwner];
all.iter().find(|x| **x as u32 == r).copied().ok_or_else(||
crate::Error::new_failed("Invalid reply from DBus server")
)
}
pub async fn release_name<'a, N: Into<BusName<'a>>>(&self, name: N) -> Result<stdintf::org_freedesktop_dbus::ReleaseNameReply, Error> {
let r = self.dbus_proxy().release_name(&name.into()).await?;
use stdintf::org_freedesktop_dbus::ReleaseNameReply::*;
let all = [Released, NonExistent, NotOwner];
all.iter().find(|x| **x as u32 == r).copied().ok_or_else(||
crate::Error::new_failed("Invalid reply from DBus server")
)
}
pub async fn add_match(&self, match_rule: MatchRule<'static>) -> Result<MsgMatch, Error> {
let m = match_rule.match_str();
self.add_match_no_cb(&m).await?;
let mi = Arc::new(MatchInner {
token: Default::default(),
cb: Default::default(),
});
let mi_weak = Arc::downgrade(&mi);
let token = self.start_receive(match_rule, Box::new(move |msg, _| {
mi_weak.upgrade().map(|mi| mi.incoming(msg)).unwrap_or(false)
}));
mi.token.store(token.0, SeqCst);
Ok(MsgMatch(mi))
}
pub async fn add_match_no_cb(&self, match_str: &str) -> Result<(), Error> {
self.dbus_proxy().add_match(match_str).await
}
pub async fn remove_match_no_cb(&self, match_str: &str) -> Result<(), Error> {
self.dbus_proxy().remove_match(match_str).await
}
pub async fn remove_match(&self, id: Token) -> Result<(), Error> {
let (mr, _) = self.stop_receive(id).ok_or_else(|| Error::new_failed("No match with that id found"))?;
self.remove_match_no_cb(&mr.match_str()).await
}
}
}
}
connimpl!(Connection, FilterCb, RepliesCb, Send);
connimpl!(LocalConnection, LocalFilterCb, LocalRepliesCb);
connimpl!(SyncConnection, SyncFilterCb, SyncRepliesCb, Send);
impl Connection {
fn filters_mut(&self) -> std::cell::RefMut<Filters<FilterCb>> { self.filters.borrow_mut() }
fn replies_mut(&self) -> std::cell::RefMut<Replies<RepliesCb>> { self.replies.borrow_mut() }
}
impl LocalConnection {
fn filters_mut(&self) -> std::cell::RefMut<Filters<LocalFilterCb>> { self.filters.borrow_mut() }
fn replies_mut(&self) -> std::cell::RefMut<Replies<LocalRepliesCb>> { self.replies.borrow_mut() }
}
impl SyncConnection {
fn filters_mut(&self) -> std::sync::MutexGuard<Filters<SyncFilterCb>> { self.filters.lock().unwrap() }
fn replies_mut(&self) -> std::sync::MutexGuard<Replies<SyncRepliesCb>> { self.replies.lock().unwrap() }
}
pub type TimeoutMakerCb = fn(timeout: Instant) -> pin::Pin<Box<dyn Future<Output=()> + Send + Sync + 'static>>;
pub type WakerCb = Box<dyn Fn() -> Result<(), ()> + Send + Sync +'static>;
pub trait NonblockReply {
type F;
fn send_with_reply(&self, msg: Message, f: Self::F) -> Result<Token, ()>;
fn cancel_reply(&self, id: Token) -> Option<Self::F>;
fn make_f<G: FnOnce(Message, &Self) + Send + 'static>(g: G) -> Self::F where Self: Sized;
fn set_timeout_maker(&mut self, f: Option<TimeoutMakerCb>) -> Option<TimeoutMakerCb>;
fn timeout_maker(&self) -> Option<TimeoutMakerCb>;
fn set_waker(&mut self, f: Option<WakerCb>) -> Option<WakerCb>;
}
pub trait Process: Sender + AsRef<Channel> {
fn process_all(&self) {
let c: &Channel = self.as_ref();
while let Some(msg) = c.pop_message() {
self.process_one(msg);
}
}
fn process_one(&self, msg: Message);
}
pub struct MsgMatch(Arc<MatchInner>);
struct MatchInner {
token: AtomicUsize,
cb: Mutex<Option<Box<dyn FnMut(Message) -> bool + Send>>>,
}
impl MatchInner {
fn incoming(&self, msg: Message) -> bool {
if let Some(ref mut cb) = self.cb.lock().unwrap().as_mut() {
cb(msg)
}
else { true }
}
}
impl MsgMatch {
pub fn msg_cb<F: FnMut(Message) -> bool + Send + 'static>(self, f: F) -> Self {
{
let mut cb = self.0.cb.lock().unwrap();
*cb = Some(Box::new(f));
}
self
}
pub fn cb<R: ReadAll, F: FnMut(Message, R) -> bool + Send + 'static>(self, mut f: F) -> Self {
self.msg_cb(move |msg| {
if let Ok(r) = R::read(&mut msg.iter_init()) {
f(msg, r)
} else { true }
})
}
pub fn msg_stream(self) -> (Self, futures::channel::mpsc::UnboundedReceiver<Message>) {
let (sender, receiver) = futures::channel::mpsc::unbounded();
(self.msg_cb(move |msg| {
sender.unbounded_send(msg).is_ok()
}), receiver)
}
pub fn stream<R: ReadAll + Send + 'static>(self) -> (Self, futures::channel::mpsc::UnboundedReceiver<(Message, R)>) {
let (sender, receiver) = futures::channel::mpsc::unbounded();
(self.cb(move |msg, r| {
sender.unbounded_send((msg, r)).is_ok()
}), receiver)
}
pub fn token(&self) -> Token { Token(self.0.token.load(SeqCst)) }
}
#[derive(Clone, Debug)]
pub struct Proxy<'a, C> {
pub destination: BusName<'a>,
pub path: Path<'a>,
pub connection: C,
pub timeout: Duration,
}
impl<'a, C> Proxy<'a, C> {
pub fn new<D: Into<BusName<'a>>, P: Into<Path<'a>>>(dest: D, path: P, timeout: Duration, connection: C) -> Self {
Proxy { destination: dest.into(), path: path.into(), timeout, connection }
}
}
struct MRAwait {
mrouter: MROuter,
token: Result<Token, ()>,
timeout: Instant,
timeoutfn: Option<TimeoutMakerCb>
}
async fn method_call_await(mra: MRAwait) -> Result<Message, Error> {
use futures::future;
let MRAwait { mrouter, token, timeout, timeoutfn } = mra;
if token.is_err() { return Err(Error::new_failed("Failed to send message")) };
let timeout = if let Some(tfn) = timeoutfn { tfn(timeout) } else { Box::pin(future::pending()) };
match future::select(mrouter, timeout).await {
future::Either::Left((r, _)) => r,
future::Either::Right(_) => Err(Error::new_custom("org.freedesktop.DBus.Error.Timeout", "Timeout waiting for reply")),
}
}
impl<'a, T, C> Proxy<'a, C>
where
T: NonblockReply,
C: std::ops::Deref<Target=T>
{
fn method_call_setup(&self, msg: Message) -> MRAwait {
let mr = Arc::new(Mutex::new(MRInner::Neither));
let mrouter = MROuter(mr.clone());
let f = T::make_f(move |msg: Message, _: &T| {
let mut inner = mr.lock().unwrap();
let old = mem::replace(&mut *inner, MRInner::Ready(Ok(msg)));
if let MRInner::Pending(waker) = old { waker.wake() }
});
let timeout = Instant::now() + self.timeout;
let token = self.connection.send_with_reply(msg, f);
let timeoutfn = self.connection.timeout_maker();
MRAwait { mrouter, token, timeout, timeoutfn }
}
pub fn method_call<'i, 'm, R: ReadAll + 'static, A: AppendAll, I: Into<Interface<'i>>, M: Into<Member<'m>>>(&self, i: I, m: M, args: A)
-> MethodReply<R> {
let mut msg = Message::method_call(&self.destination, &self.path, &i.into(), &m.into());
args.append(&mut IterAppend::new(&mut msg));
let mra = self.method_call_setup(msg);
let r = method_call_await(mra);
let r = futures::FutureExt::map(r, |r| -> Result<R, Error> { r.and_then(|rmsg| rmsg.read_all()) } );
MethodReply::new(r)
}
}
enum MRInner {
Ready(Result<Message, Error>),
Pending(task::Waker),
Neither,
}
struct MROuter(Arc<Mutex<MRInner>>);
impl Future for MROuter {
type Output = Result<Message, Error>;
fn poll(self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Self::Output> {
let mut inner = self.0.lock().unwrap();
let r = mem::replace(&mut *inner, MRInner::Neither);
if let MRInner::Ready(r) = r { task::Poll::Ready(r) }
else {
mem::replace(&mut *inner, MRInner::Pending(ctx.waker().clone()));
return task::Poll::Pending
}
}
}
pub struct MethodReply<T>(pin::Pin<Box<dyn Future<Output=Result<T, Error>> + Send + 'static>>);
impl<T> MethodReply<T> {
fn new<Fut: Future<Output=Result<T, Error>> + Send + 'static>(fut: Fut) -> Self {
MethodReply(Box::pin(fut))
}
}
impl<T> Future for MethodReply<T> {
type Output = Result<T, Error>;
fn poll(mut self: pin::Pin<&mut Self>, ctx: &mut task::Context) -> task::Poll<Result<T, Error>> {
self.0.as_mut().poll(ctx)
}
}
impl<T: 'static> MethodReply<T> {
pub fn and_then<T2>(self, f: impl FnOnce(T) -> Result<T2, Error> + Send + Sync + 'static) -> MethodReply<T2> {
MethodReply(Box::pin(async move {
let x = self.0.await?;
f(x)
}))
}
}
#[test]
fn test_conn_send_sync() {
fn is_send<T: Send>() {}
fn is_sync<T: Sync>() {}
is_send::<Connection>();
is_send::<SyncConnection>();
is_sync::<SyncConnection>();
is_send::<MsgMatch>();
}