dbus_crossroads/
context.rs

1use std::marker::PhantomData;
2use dbus::arg::AppendAll;
3use dbus::channel::Sender;
4use std::sync::Arc;
5use crate::{MethodErr, utils::Dbg};
6
7/// Context is the struct that accompanies you through your method call handler,
8/// providing helpful information about the message sent from the client, as well as
9/// some methods to send extra messages (typically signals) in return.
10#[derive(Debug)]
11pub struct Context {
12    path: dbus::Path<'static>,
13    interface: Option<dbus::strings::Interface<'static>>,
14    method: dbus::strings::Member<'static>,
15    message: dbus::Message,
16
17    has_error: bool,
18    reply: Option<dbus::Message>,
19    send_extra: Vec<dbus::Message>,
20    send_on_drop: Option<Dbg<Arc<dyn Sender + Send + Sync>>>,
21}
22
23impl Context {
24    /// Creates a new Context.
25    ///
26    /// Usually you're not creating your own context, as the crossroads instance is creating one for you.
27    pub fn new(msg: dbus::Message) -> Option<Self> {
28        if msg.msg_type() != dbus::MessageType::MethodCall { return None; }
29        let p = msg.path()?.into_static();
30        let i = msg.interface().map(|i| i.into_static());
31        let m = msg.member()?.into_static();
32        Some(Context {
33            path: p,
34            interface: i,
35            method: m,
36            message: msg,
37            reply: None,
38            send_on_drop: None,
39            send_extra: vec!(),
40            has_error: false,
41        })
42    }
43
44    /// Convenience method that sets an error reply if the closure returns an error.
45    pub fn check<R, F: FnOnce(&mut Context) -> Result<R, MethodErr>>(&mut self, f: F) -> Result<R, ()> {
46        f(self).map_err(|e| { self.reply_err(e); })
47    }
48
49    /// If the reply is not already set, creates a new method return message and calls the closure
50    /// so that the closure can fill in the arguments.
51    pub fn do_reply<F: FnOnce(&mut dbus::Message)>(&mut self, f: F) {
52        if self.message.get_no_reply() { return; }
53        if self.reply.is_some() { return; }
54        let mut msg = self.message.method_return();
55        f(&mut msg);
56        self.reply = Some(msg);
57    }
58
59    /// Replies ok to the incoming message, if the reply is not already set.
60    /// This is what you'll normally have last in your async method.
61    ///
62    /// Returns PhantomData just to aid the type system.
63    pub (crate) fn reply_ok<OA: AppendAll>(&mut self, oa: OA) -> PhantomData<OA> {
64        self.do_reply(|msg| { msg.append_all(oa); });
65        PhantomData
66    }
67
68    /// Replies to the incoming message, if the reply is not already set.
69    /// This is what you'll normally have last in your async method.
70    ///
71    /// Returns PhantomData just to aid the type system.
72    pub fn reply<OA: AppendAll>(&mut self, result: Result<OA, MethodErr>) -> PhantomData<OA> {
73        match result {
74            Ok(oa) => { self.reply_ok(oa); },
75            Err(e) => { self.reply_err(e); },
76        };
77        PhantomData
78    }
79
80    /// Replies to the incoming message with an error.
81    pub (crate) fn reply_err(&mut self, err: MethodErr) {
82        self.has_error = true;
83        if !self.message.get_no_reply() {
84            self.reply = Some(err.to_message(&self.message))
85        };
86    }
87
88    /// Low-level function to set a reply
89    ///
90    /// You should probably prefer do_reply, or reply_ok / reply_err for async methods.
91    pub fn set_reply(&mut self, msg: Option<dbus::Message>, check_no_reply: bool, check_set: bool) {
92        if check_no_reply && self.message.get_no_reply() { return; }
93        if check_set && self.reply.is_some() { return; }
94        self.reply = msg;
95    }
96
97    /// Low-level function to flush set messages
98    ///
99    /// This is called internally, you should probably not use it.
100    pub fn flush_messages<S: dbus::channel::Sender + ?Sized>(&mut self, conn: &S) -> Result<(), ()> {
101        if let Some(msg) = self.reply.take() {
102            conn.send(msg)?;
103        }
104        for msg in self.send_extra.drain(..) {
105            conn.send(msg)?;
106        }
107        Ok(())
108    }
109
110    /// Makes a new signal with the current interface and path
111    pub fn make_signal<'b, A, N>(&self, name: N, args: A) -> dbus::Message
112    where A: dbus::arg::AppendAll, N: Into<dbus::strings::Member<'b>> {
113        let mut msg = dbus::Message::signal(&self.path, self.interface.as_ref().unwrap(), &name.into());
114        msg.append_all(args);
115        msg
116    }
117
118    /// Adds an extra message to send together with the message reply, e g, a custom signal.
119    pub fn push_msg(&mut self, msg: dbus::Message) { self.send_extra.push(msg); }
120
121    /// The current object path.
122    pub fn path(&self) -> &dbus::Path<'static> { &self.path }
123
124    /// The current interface name.
125    ///
126    /// The D-Bus specfication allows for the interface to be unspecified, hence this returns an
127    /// option. This is very rarely used in practice.
128    pub fn interface(&self) -> Option<&dbus::strings::Interface<'static>> { self.interface.as_ref() }
129
130    /// The current method name.
131    pub fn method(&self) -> &dbus::strings::Member<'static> { &self.method }
132
133    /// The message that caused this method to be called.
134    pub fn message(&self) -> &dbus::Message { &self.message }
135
136    /// True if a reply (error or method return) has been set.
137    pub fn has_reply(&self) -> bool { self.reply.is_some() }
138
139    /// Returns true is "reply_err" has been called, or "check" ever returned an error
140    pub fn has_error(&self) -> bool { self.has_error }
141
142    pub (crate) fn set_send_on_drop(&mut self, value: Arc<dyn Sender + Send + Sync>) {
143        self.send_on_drop = Some(Dbg(value));
144    }
145}
146
147impl Drop for Context {
148    fn drop(&mut self) {
149        if let Some(sender) = self.send_on_drop.take() {
150            let _ = self.flush_messages(&*sender.0);
151        }
152    }
153}