1 | use crate::{Message, MessageType}; |
2 | use crate::strings::{BusName, Path, Interface, Member}; |
3 | use crate::message::parser; |
4 | |
5 | #[derive (Clone, Debug, Default)] |
6 | /// A "match rule", that can match Messages on its headers. |
7 | /// |
8 | /// A field set to "None" means no filter for that header, |
9 | /// a field set to "Some(_)" must match exactly. |
10 | pub struct MatchRule<'a> { |
11 | /// Match on message type (you typically want to do this) |
12 | pub msg_type: Option<MessageType>, |
13 | /// Match on message sender |
14 | pub sender: Option<BusName<'a>>, |
15 | /// If false (the default), match if sender could possibly match, due to mismatch between unique names and taken bus names |
16 | pub strict_sender: bool, |
17 | /// Match on message object path |
18 | pub path: Option<Path<'a>>, |
19 | /// If true, will match all subpaths to the path as well as the path itself. Defaults to false. |
20 | pub path_is_namespace: bool, |
21 | /// Match on message interface |
22 | pub interface: Option<Interface<'a>>, |
23 | /// Match on message member (signal or method name) |
24 | pub member: Option<Member<'a>>, |
25 | /// If true, also receive messages not intended for us. Defaults to false. |
26 | pub eavesdrop: bool, |
27 | _more_fields_may_come: (), |
28 | } |
29 | |
30 | fn msg_type_str(m: MessageType) -> &'static str { |
31 | use crate::MessageType::*; |
32 | match m { |
33 | Signal => "signal" , |
34 | MethodCall => "method_call" , |
35 | MethodReturn => "method_return" , |
36 | Error => "error" , |
37 | } |
38 | } |
39 | |
40 | |
41 | impl<'a> MatchRule<'a> { |
42 | /// Make a string which you can use in the call to "add_match". |
43 | pub fn match_str(&self) -> String { |
44 | let mut v = vec!(); |
45 | if let Some(x) = self.msg_type { v.push(("type" , msg_type_str(x))) }; |
46 | if let Some(ref x) = self.sender { v.push(("sender" , &x)) }; |
47 | let pn = if self.path_is_namespace { "path_namespace" } else { "path" }; |
48 | if let Some(ref x) = self.path { v.push((pn, &x)) }; |
49 | if let Some(ref x) = self.interface { v.push(("interface" , &x)) }; |
50 | if let Some(ref x) = self.member { v.push(("member" , &x)) }; |
51 | if self.eavesdrop { v.push(("eavesdrop" , "true" )) }; |
52 | |
53 | // For now we don't need to worry about internal quotes in strings as those are not valid names. |
54 | // If we start matching against arguments, we need to worry. |
55 | let v: Vec<_> = v.into_iter().map(|(k, v)| format!(" {}=' {}'" , k, v)).collect(); |
56 | v.join("," ) |
57 | } |
58 | |
59 | fn path_match(&self, msg: &Message) -> bool { |
60 | if let Some(ref x) = self.path { |
61 | if let Some(ref p) = msg.path() { |
62 | if x != p { |
63 | if self.path_is_namespace { |
64 | p.starts_with(&**x) && &p[x.len()..x.len() + 1] == "/" |
65 | } else { false } |
66 | } else { true } |
67 | } else { false } |
68 | } else { true } |
69 | } |
70 | |
71 | /// Returns whether or not the message matches the rule. |
72 | pub fn matches(&self, msg: &Message) -> bool { |
73 | if let Some(x) = self.msg_type { if x != msg.msg_type() { return false; } }; |
74 | |
75 | if let Some(ref x) = self.sender { |
76 | if let Some(s) = msg.sender() { |
77 | let check = self.strict_sender || (s.starts_with(":" ) == x.starts_with(":" )); |
78 | if check && s != *x { return false; } |
79 | } else if self.strict_sender { return false; } |
80 | }; |
81 | if !self.path_match(msg) { return false; } |
82 | if self.interface.is_some() && msg.interface() != self.interface { return false; }; |
83 | if self.member.is_some() && msg.member() != self.member { return false; }; |
84 | true |
85 | } |
86 | |
87 | /// Create a new struct which matches every message. |
88 | pub fn new() -> Self { Default::default() } |
89 | |
90 | /// Create a new struct which matches every incoming method call message. |
91 | pub fn new_method_call() -> Self { |
92 | let mut m = Self::new(); |
93 | m.msg_type = Some(MessageType::MethodCall); |
94 | m |
95 | } |
96 | |
97 | /// Create a new struct which matches signals on the interface and member name. |
98 | pub fn new_signal<I: Into<Interface<'a>>, N: Into<Member<'a>>>(intf: I, name: N) -> Self { |
99 | let mut m = Self::new(); |
100 | m.msg_type = Some(MessageType::Signal); |
101 | m.interface = Some(intf.into()); |
102 | m.member = Some(name.into()); |
103 | m |
104 | } |
105 | |
106 | /// Returns a clone with no borrowed references |
107 | pub fn static_clone(&self) -> MatchRule<'static> { |
108 | MatchRule { |
109 | msg_type: self.msg_type, |
110 | sender: self.sender.as_ref().map(|x| x.clone().into_static()), |
111 | strict_sender: self.strict_sender, |
112 | path: self.path.as_ref().map(|x| x.clone().into_static()), |
113 | interface: self.interface.as_ref().map(|x| x.clone().into_static()), |
114 | member: self.member.as_ref().map(|x| x.clone().into_static()), |
115 | path_is_namespace: self.path_is_namespace, |
116 | eavesdrop: self.eavesdrop, |
117 | _more_fields_may_come: (), |
118 | } |
119 | } |
120 | |
121 | /// Enables eavesdropping for the generated message. |
122 | /// You probably want to use [BecomeMonitor](https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-become-monitor) instead |
123 | pub fn with_eavesdrop(mut self) -> Self { |
124 | self.eavesdrop = true; |
125 | self |
126 | } |
127 | |
128 | /// Sets the MatchRule to match on the message sender |
129 | pub fn with_sender(mut self, sender: impl Into<BusName<'a>>) -> Self { |
130 | self.sender = Some(sender.into()); |
131 | self |
132 | } |
133 | |
134 | /// Sets the MatchRule to match on the message sender and be strict |
135 | pub fn with_strict_sender(mut self, sender: impl Into<BusName<'a>>) -> Self { |
136 | self.sender = Some(sender.into()); |
137 | self.strict_sender = true; |
138 | self |
139 | } |
140 | |
141 | /// Sets the MatchRule to match on the message path and treat it as a namespace |
142 | pub fn with_namespaced_path(mut self, path: impl Into<Path<'a>>) -> Self { |
143 | self.path = Some(path.into()); |
144 | self.path_is_namespace = true; |
145 | self |
146 | } |
147 | |
148 | /// Sets the MatchRule to match on the message path |
149 | pub fn with_path(mut self, path: impl Into<Path<'a>>) -> Self { |
150 | self.path = Some(path.into()); |
151 | self |
152 | } |
153 | |
154 | /// Sets the MatchRule to match on the message interface |
155 | pub fn with_interface(mut self, intf: impl Into<Interface<'a>>) -> Self { |
156 | self.interface = Some(intf.into()); |
157 | self |
158 | } |
159 | |
160 | /// Sets the MatchRule to match on the message member |
161 | pub fn with_member(mut self, member: impl Into<Member<'a>>) -> Self { |
162 | self.member = Some(member.into()); |
163 | self |
164 | } |
165 | |
166 | /// Sets the MatchRule to match on the message type. This will usually be `"signal"` |
167 | pub fn with_type(mut self, ty: MessageType) -> Self { |
168 | self.msg_type = Some(ty); |
169 | self |
170 | } |
171 | |
172 | /// Tries parsing a MatchRule from a String. Please note however that not all features supported |
173 | /// by DBus are supported by dbus-rs (yet). args and destinations are not supported yet. |
174 | pub fn parse(text: &'a str) -> Result<Self, parser::Error> { |
175 | parser::Parser::new(text)?.parse() |
176 | } |
177 | } |