summaryrefslogtreecommitdiffstats
path: root/third_party/rust/dbus/src/dispatcher.rs
blob: eda78ed58691a9818134baed95327f4146b4b526 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use crate::{Message, MessageType, Error, to_c_str, c_str_to_slice};
use std::ptr;

use std::collections::HashMap;

/// [Unstable and Experimental]
pub trait MessageDispatcherConfig: Sized {
    /// The type of method reply stored inside the dispatcher
    type Reply;

    /// Called when a method call has received a reply.
    fn on_reply(reply: Self::Reply, msg: Message, dispatcher: &mut MessageDispatcher<Self>);

    /// Called when a signal is received.
    ///
    /// Defaults to doing nothing.
    #[allow(unused_variables)]
    fn on_signal(msg: Message, dispatcher: &mut MessageDispatcher<Self>) {}

    /// Called when a method call is received.
    ///
    /// Defaults to calling default_dispatch.
    fn on_method_call(msg: Message, dispatcher: &mut MessageDispatcher<Self>) {
        if let Some(reply) = MessageDispatcher::<Self>::default_dispatch(&msg) {
            Self::on_send(reply, dispatcher);
        }
    }

    /// Called in the other direction, i e, when a message should be sent over the connection.
    fn on_send(msg: Message, dispatcher: &mut MessageDispatcher<Self>);
}

/// Dummy implementation
impl MessageDispatcherConfig for () {
    type Reply = ();
    fn on_reply(_: Self::Reply, _: Message, _: &mut MessageDispatcher<Self>) { unreachable!() }
    fn on_send(_: Message, _: &mut MessageDispatcher<Self>) { unreachable!() }
}

/// [Unstable and Experimental] Meant for usage with RxTx.
pub struct MessageDispatcher<C: MessageDispatcherConfig> {
    waiting_replies: HashMap<u32, C::Reply>,
    inner: C,
}

impl<C: MessageDispatcherConfig> MessageDispatcher<C> {

    /// Creates a new message dispatcher.
    pub fn new(inner: C) -> Self { MessageDispatcher {
        waiting_replies: HashMap::new(),
        inner: inner,
    } }

    /// "Inner" accessor
    pub fn inner(&self) -> &C { &self.inner }

    /// "Inner" mutable accessor
    pub fn inner_mut(&mut self) -> &mut C { &mut self.inner }

    /// Adds a waiting reply to a method call. func will be called when a method reply is dispatched.
    pub fn add_reply(&mut self, serial: u32, func: C::Reply) {
        if let Some(_) = self.waiting_replies.insert(serial, func) {
            // panic because we're overwriting something else, or just ignore?
        }
    }

    /// Cancels a waiting reply.
    pub fn cancel_reply(&mut self, serial: u32) -> Option<C::Reply> {
        self.waiting_replies.remove(&serial)
    }


    /// Dispatch an incoming message.
    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 => {},
            MessageType::Invalid => unreachable!(),
        }
    }

    /// Handles what we need to be a good D-Bus citizen.
    ///
    /// Call this if you have not handled the message yourself:
    /// * It handles calls to org.freedesktop.DBus.Peer.
    /// * For other method calls, it sends an error reply back that the method was unknown.
    pub fn default_dispatch(m: &Message) -> Option<Message> {
        Self::peer(&m)
            .or_else(|| Self::unknown_method(&m))
    }

    /// Replies if this is a call to org.freedesktop.DBus.Peer, otherwise returns None.
    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 }
    }

    /// For method calls, it replies that the method was unknown, otherwise returns None.
    pub fn unknown_method(m: &Message) -> Option<Message> {
        if m.msg_type() != MessageType::MethodCall { return None; }
        // if m.get_no_reply() { return None; } // The reference implementation does not do this?
        Some(m.error(&"org.freedesktop.DBus.Error.UnknownMethod".into(), &to_c_str("Path, Interface, or Method does not exist")))
    }
}