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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
use crate::{Message, MessageType};
use crate::strings::{BusName, Path, Interface, Member};
use crate::message::parser;

#[derive(Clone, Debug, Default)]
/// A "match rule", that can match Messages on its headers.
///
/// A field set to "None" means no filter for that header,
/// a field set to "Some(_)" must match exactly.
pub struct MatchRule<'a> {
    /// Match on message type (you typically want to do this)
    pub msg_type: Option<MessageType>,
    /// Match on message sender
    pub sender: Option<BusName<'a>>,
    /// If false (the default), match if sender could possibly match, due to mismatch between unique names and taken bus names
    pub strict_sender: bool,
    /// Match on message object path
    pub path: Option<Path<'a>>,
    /// If true, will match all subpaths to the path as well as the path itself. Defaults to false.
    pub path_is_namespace: bool,
    /// Match on message interface
    pub interface: Option<Interface<'a>>,
    /// Match on message member (signal or method name)
    pub member: Option<Member<'a>>,
    /// If true, also receive messages not intended for us. Defaults to false.
    pub eavesdrop: bool,
    _more_fields_may_come: (),
}

fn msg_type_str(m: MessageType) -> &'static str {
    use crate::MessageType::*;
    match m {
        Signal => "signal",
        MethodCall => "method_call",
        MethodReturn => "method_return",
        Error => "error",
    }
}


impl<'a> MatchRule<'a> {
    /// Make a string which you can use in the call to "add_match".
    pub fn match_str(&self) -> String {
        let mut v = vec!();
        if let Some(x) = self.msg_type { v.push(("type", msg_type_str(x))) };
        if let Some(ref x) = self.sender { v.push(("sender", &x)) };
        let pn = if self.path_is_namespace { "path_namespace" } else { "path" };
        if let Some(ref x) = self.path { v.push((pn, &x)) };
        if let Some(ref x) = self.interface { v.push(("interface", &x)) };
        if let Some(ref x) = self.member { v.push(("member", &x)) };
        if self.eavesdrop { v.push(("eavesdrop", "true")) };

        // For now we don't need to worry about internal quotes in strings as those are not valid names.
        // If we start matching against arguments, we need to worry.
        let v: Vec<_> = v.into_iter().map(|(k, v)| format!("{}='{}'", k, v)).collect();
        v.join(",")
    }

    fn path_match(&self, msg: &Message) -> bool {
        if let Some(ref x) = self.path {
            if let Some(ref p) = msg.path() {
                if x != p {
                    if self.path_is_namespace {
                        p.starts_with(&**x) && &p[x.len()..x.len() + 1] == "/"
                    } else { false }
                } else { true }
            } else { false }
        } else { true }
    }

    /// Returns whether or not the message matches the rule.
    pub fn matches(&self, msg: &Message) -> bool {
        if let Some(x) = self.msg_type { if x != msg.msg_type() { return false; } };

        if let Some(ref x) = self.sender {
            if let Some(s) = msg.sender() {
                let check = self.strict_sender || (s.starts_with(":") == x.starts_with(":"));
                if check && s != *x { return false; }
            } else if self.strict_sender { return false; }
        };
        if !self.path_match(msg) { return false; }
        if self.interface.is_some() && msg.interface() != self.interface { return false; };
        if self.member.is_some() && msg.member() != self.member { return false; };
        true
    }

    /// Create a new struct which matches every message.
    pub fn new() -> Self { Default::default() }

    /// Create a new struct which matches every incoming method call message.
    pub fn new_method_call() -> Self {
        let mut m = Self::new();
        m.msg_type = Some(MessageType::MethodCall);
        m
    }

    /// Create a new struct which matches signals on the interface and member name.
    pub fn new_signal<I: Into<Interface<'a>>, N: Into<Member<'a>>>(intf: I, name: N) -> Self {
        let mut m = Self::new();
        m.msg_type = Some(MessageType::Signal);
        m.interface = Some(intf.into());
        m.member = Some(name.into());
        m
    }

    /// Returns a clone with no borrowed references
    pub fn static_clone(&self) -> MatchRule<'static> {
        MatchRule {
            msg_type: self.msg_type,
            sender: self.sender.as_ref().map(|x| x.clone().into_static()),
            strict_sender: self.strict_sender,
            path: self.path.as_ref().map(|x| x.clone().into_static()),
            interface: self.interface.as_ref().map(|x| x.clone().into_static()),
            member: self.member.as_ref().map(|x| x.clone().into_static()),
            path_is_namespace: self.path_is_namespace,
            eavesdrop: self.eavesdrop,
            _more_fields_may_come: (),
        }
    }

    /// Enables eavesdropping for the generated message.
    /// You probably want to use [BecomeMonitor](https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-become-monitor) instead
    pub fn with_eavesdrop(mut self) -> Self {
        self.eavesdrop = true;
        self
    }

    /// Sets the MatchRule to match on the message sender
    pub fn with_sender(mut self, sender: impl Into<BusName<'a>>) -> Self {
        self.sender = Some(sender.into());
        self
    }

    /// Sets the MatchRule to match on the message sender and be strict
    pub fn with_strict_sender(mut self, sender: impl Into<BusName<'a>>) -> Self {
        self.sender = Some(sender.into());
        self.strict_sender = true;
        self
    }

    /// Sets the MatchRule to match on the message path and treat it as a namespace
    pub fn with_namespaced_path(mut self, path: impl Into<Path<'a>>) -> Self {
        self.path = Some(path.into());
        self.path_is_namespace = true;
        self
    }

    /// Sets the MatchRule to match on the message path
    pub fn with_path(mut self, path: impl Into<Path<'a>>) -> Self {
        self.path = Some(path.into());
        self
    }

    /// Sets the MatchRule to match on the message interface
    pub fn with_interface(mut self, intf: impl Into<Interface<'a>>) -> Self {
        self.interface = Some(intf.into());
        self
    }

    /// Sets the MatchRule to match on the message member
    pub fn with_member(mut self, member: impl Into<Member<'a>>) -> Self {
        self.member = Some(member.into());
        self
    }

    /// Sets the MatchRule to match on the message type. This will usually be `"signal"`
    pub fn with_type(mut self, ty: MessageType) -> Self {
        self.msg_type = Some(ty);
        self
    }

    /// Tries parsing a MatchRule from a String. Please note however that not all features supported
    /// by DBus are supported by dbus-rs (yet). args and destinations are not supported yet.
    pub fn parse(text: &'a str) -> Result<Self, parser::Error> {
        parser::Parser::new(text)?.parse()
    }
}