zbus/
match_rule.rs

1use core::panic;
2use std::{
3    convert::TryFrom,
4    fmt::{Display, Write},
5    ops::Deref,
6};
7
8use serde::{de, Deserialize, Serialize};
9use static_assertions::assert_impl_all;
10use zvariant::Structure;
11
12use crate::{
13    names::{BusName, InterfaceName, MemberName, UniqueName},
14    zvariant::{ObjectPath, Str, Type},
15    Error, MatchRuleBuilder, MessageType, Result,
16};
17
18/// A bus match rule for subscribing to specific messages.
19///
20/// This is mainly used by peer to subscribe to specific signals as by default the bus will not
21/// send out most broadcasted signals. This API is intended to make it easy to create and parse
22/// match rules. See the [match rules section of the D-Bus specification][mrs] for a description of
23/// each possible element of a match rule.
24///
25/// # Examples
26///
27/// ```
28/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
29/// # use zbus::MatchRule;
30/// use std::convert::TryFrom;
31///
32/// // Let's take the most typical example of match rule to subscribe to properties' changes:
33/// let rule = MatchRule::builder()
34///     .msg_type(zbus::MessageType::Signal)
35///     .sender("org.freedesktop.DBus")?
36///     .interface("org.freedesktop.DBus.Properties")?
37///     .member("PropertiesChanged")?
38///     .add_arg("org.zbus")?
39///     // Sometimes it's useful to match empty strings (null check).
40///     .add_arg("")?
41///     .build();
42/// let rule_str = rule.to_string();
43/// assert_eq!(
44///     rule_str,
45///     "type='signal',\
46///      sender='org.freedesktop.DBus',\
47///      interface='org.freedesktop.DBus.Properties',\
48///      member='PropertiesChanged',\
49///      arg0='org.zbus',\
50///      arg1=''",
51/// );
52///
53/// // Let's parse it back.
54/// let parsed_rule = MatchRule::try_from(rule_str.as_str())?;
55/// assert_eq!(rule, parsed_rule);
56///
57/// // Now for `ObjectManager::InterfacesAdded` signal.
58/// let rule = MatchRule::builder()
59///     .msg_type(zbus::MessageType::Signal)
60///     .sender("org.zbus")?
61///     .interface("org.freedesktop.DBus.ObjectManager")?
62///     .member("InterfacesAdded")?
63///     .arg_path(0, "/org/zbus/NewPath")?
64///     .build();
65/// let rule_str = rule.to_string();
66/// assert_eq!(
67///     rule_str,
68///     "type='signal',\
69///      sender='org.zbus',\
70///      interface='org.freedesktop.DBus.ObjectManager',\
71///      member='InterfacesAdded',\
72///      arg0path='/org/zbus/NewPath'",
73/// );
74///
75/// // Let's parse it back.
76/// let parsed_rule = MatchRule::try_from(rule_str.as_str())?;
77/// assert_eq!(rule, parsed_rule);
78///
79/// # Ok(())
80/// # }
81/// ```
82///
83/// # Caveats
84///
85/// The `PartialEq` implementation assumes arguments in both rules are in the same order.
86///
87/// [mrs]: https://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules
88#[derive(Clone, Debug, PartialEq, Eq, Hash, Type)]
89#[zvariant(signature = "s")]
90pub struct MatchRule<'m> {
91    pub(crate) msg_type: Option<MessageType>,
92    pub(crate) sender: Option<BusName<'m>>,
93    pub(crate) interface: Option<InterfaceName<'m>>,
94    pub(crate) member: Option<MemberName<'m>>,
95    pub(crate) path_spec: Option<MatchRulePathSpec<'m>>,
96    pub(crate) destination: Option<UniqueName<'m>>,
97    pub(crate) args: Vec<(u8, Str<'m>)>,
98    pub(crate) arg_paths: Vec<(u8, ObjectPath<'m>)>,
99    pub(crate) arg0namespace: Option<InterfaceName<'m>>,
100    pub(crate) arg0ns: Option<Str<'m>>,
101}
102
103assert_impl_all!(MatchRule<'_>: Send, Sync, Unpin);
104
105impl<'m> MatchRule<'m> {
106    /// Create a builder for `MatchRuleBuilder`.
107    pub fn builder() -> MatchRuleBuilder<'m> {
108        MatchRuleBuilder::new()
109    }
110
111    /// The sender, if set.
112    pub fn sender(&self) -> Option<&BusName<'_>> {
113        self.sender.as_ref()
114    }
115
116    /// The message type, if set.
117    pub fn msg_type(&self) -> Option<MessageType> {
118        self.msg_type
119    }
120
121    /// The interfac, if set.
122    pub fn interface(&self) -> Option<&InterfaceName<'_>> {
123        self.interface.as_ref()
124    }
125
126    /// The member name if set.
127    pub fn member(&self) -> Option<&MemberName<'_>> {
128        self.member.as_ref()
129    }
130
131    /// The path or path namespace, if set.
132    pub fn path_spec(&self) -> Option<&MatchRulePathSpec<'_>> {
133        self.path_spec.as_ref()
134    }
135
136    /// The destination, if set.
137    pub fn destination(&self) -> Option<&UniqueName<'_>> {
138        self.destination.as_ref()
139    }
140
141    /// The arguments.
142    pub fn args(&self) -> &[(u8, Str<'_>)] {
143        self.args.as_ref()
144    }
145
146    /// The argument paths.
147    pub fn arg_paths(&self) -> &[(u8, ObjectPath<'_>)] {
148        self.arg_paths.as_ref()
149    }
150
151    /// Match messages whose first argument is within the specified namespace.
152    ///
153    /// This function is deprecated because the choice of `InterfaceName` was too restrictive.
154    #[deprecated = "use arg0ns instead"]
155    pub fn arg0namespace(&self) -> Option<&InterfaceName<'_>> {
156        self.arg0namespace.as_ref()
157    }
158
159    /// Match messages whose first argument is within the specified namespace.
160    pub fn arg0ns(&self) -> Option<&Str<'m>> {
161        self.arg0ns.as_ref()
162    }
163
164    /// Creates an owned clone of `self`.
165    pub fn to_owned(&self) -> MatchRule<'static> {
166        MatchRule {
167            msg_type: self.msg_type,
168            sender: self.sender.as_ref().map(|s| s.to_owned()),
169            interface: self.interface.as_ref().map(|i| i.to_owned()),
170            member: self.member.as_ref().map(|m| m.to_owned()),
171            path_spec: self.path_spec.as_ref().map(|p| p.to_owned()),
172            destination: self.destination.as_ref().map(|d| d.to_owned()),
173            args: self.args.iter().map(|(i, s)| (*i, s.to_owned())).collect(),
174            arg_paths: self
175                .arg_paths
176                .iter()
177                .map(|(i, p)| (*i, p.to_owned()))
178                .collect(),
179            arg0namespace: self.arg0namespace.as_ref().map(|a| a.to_owned()),
180            arg0ns: self.arg0ns.as_ref().map(|a| a.to_owned()),
181        }
182    }
183
184    /// Creates an owned clone of `self`.
185    pub fn into_owned(self) -> MatchRule<'static> {
186        MatchRule {
187            msg_type: self.msg_type,
188            sender: self.sender.map(|s| s.into_owned()),
189            interface: self.interface.map(|i| i.into_owned()),
190            member: self.member.map(|m| m.into_owned()),
191            path_spec: self.path_spec.map(|p| p.into_owned()),
192            destination: self.destination.map(|d| d.into_owned()),
193            args: self
194                .args
195                .into_iter()
196                .map(|(i, s)| (i, s.into_owned()))
197                .collect(),
198            arg_paths: self
199                .arg_paths
200                .into_iter()
201                .map(|(i, p)| (i, p.into_owned()))
202                .collect(),
203            arg0namespace: self.arg0namespace.map(|a| a.into_owned()),
204            arg0ns: self.arg0ns.map(|a| a.into_owned()),
205        }
206    }
207
208    /// Match the given message against this rule.
209    ///
210    /// # Caveats
211    ///
212    /// Since this method doesn't have any knowledge of names on the bus (or even connection to a
213    /// bus) matching always succeeds for:
214    ///
215    /// * `sender` in the rule (if set) that is a well-known name. The `sender` on a message is
216    ///   always a unique name.
217    /// * `destination` in the rule when `destination` on the `msg` is a well-known name. The
218    ///   `destination` on match rule is always a unique name.
219    pub fn matches(&self, msg: &zbus::Message) -> Result<bool> {
220        let hdr = msg.header()?;
221
222        // Start with message type.
223        if let Some(msg_type) = self.msg_type() {
224            if msg_type != msg.message_type() {
225                return Ok(false);
226            }
227        }
228
229        // Then check sender.
230        if let Some(sender) = self.sender() {
231            match sender {
232                BusName::Unique(name) if Some(name) != hdr.sender()? => {
233                    return Ok(false);
234                }
235                BusName::Unique(_) => (),
236                // We can't match against a well-known name.
237                BusName::WellKnown(_) => (),
238            }
239        }
240
241        // The interface.
242        if let Some(interface) = self.interface() {
243            match msg.interface().as_ref() {
244                Some(msg_interface) if interface != msg_interface => return Ok(false),
245                Some(_) => (),
246                None => return Ok(false),
247            }
248        }
249
250        // The member.
251        if let Some(member) = self.member() {
252            match msg.member().as_ref() {
253                Some(msg_member) if member != msg_member => return Ok(false),
254                Some(_) => (),
255                None => return Ok(false),
256            }
257        }
258
259        // The destination.
260        if let Some(destination) = self.destination() {
261            match hdr.destination()? {
262                Some(BusName::Unique(name)) if destination != name => {
263                    return Ok(false);
264                }
265                Some(BusName::Unique(_)) | None => (),
266                // We can't match against a well-known name.
267                Some(BusName::WellKnown(_)) => (),
268            };
269        }
270
271        // The path.
272        if let Some(path_spec) = self.path_spec() {
273            let msg_path = match msg.path() {
274                Some(p) => p,
275                None => return Ok(false),
276            };
277            match path_spec {
278                MatchRulePathSpec::Path(path) if path != &msg_path => return Ok(false),
279                MatchRulePathSpec::PathNamespace(path_ns)
280                    if !msg_path.starts_with(path_ns.as_str()) =>
281                {
282                    return Ok(false);
283                }
284                MatchRulePathSpec::Path(_) | MatchRulePathSpec::PathNamespace(_) => (),
285            }
286        }
287
288        // The arg0 namespace.
289        if let Some(arg0_ns) = self.arg0ns() {
290            if let Ok(arg0) = msg.body_unchecked::<BusName<'_>>() {
291                match arg0.strip_prefix(arg0_ns.as_str()) {
292                    None => return Ok(false),
293                    Some(s) if !s.is_empty() && !s.starts_with('.') => return Ok(false),
294                    _ => (),
295                }
296            } else {
297                return Ok(false);
298            }
299        }
300
301        // Args
302        if self.args().is_empty() && self.arg_paths().is_empty() {
303            return Ok(true);
304        }
305        let structure = match msg.body::<Structure<'_>>() {
306            Ok(s) => s,
307            Err(_) => return Ok(false),
308        };
309        let args = structure.fields();
310
311        for (i, arg) in self.args() {
312            match args.get(*i as usize) {
313                Some(msg_arg) => match <&str>::try_from(msg_arg) {
314                    Ok(msg_arg) if arg != msg_arg => return Ok(false),
315                    Ok(_) => (),
316                    Err(_) => return Ok(false),
317                },
318                None => return Ok(false),
319            }
320        }
321
322        // Path args
323        for (i, path) in self.arg_paths() {
324            match args.get(*i as usize) {
325                Some(msg_arg) => match <ObjectPath<'_>>::try_from(msg_arg) {
326                    Ok(msg_arg) if *path != msg_arg => return Ok(false),
327                    Ok(_) => (),
328                    Err(_) => return Ok(false),
329                },
330                None => return Ok(false),
331            }
332        }
333
334        Ok(true)
335    }
336}
337
338impl Display for MatchRule<'_> {
339    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340        let mut first_component = true;
341        if let Some(msg_type) = self.msg_type() {
342            let type_str = match msg_type {
343                MessageType::Error => "error",
344                MessageType::Invalid => panic!("invalid message type"),
345                MessageType::MethodCall => "method_call",
346                MessageType::MethodReturn => "method_return",
347                MessageType::Signal => "signal",
348            };
349            write_match_rule_string_component(f, "type", type_str, &mut first_component)?;
350        }
351        if let Some(sender) = self.sender() {
352            write_match_rule_string_component(f, "sender", sender, &mut first_component)?;
353        }
354        if let Some(interface) = self.interface() {
355            write_match_rule_string_component(f, "interface", interface, &mut first_component)?;
356        }
357        if let Some(member) = self.member() {
358            write_match_rule_string_component(f, "member", member, &mut first_component)?;
359        }
360        if let Some(destination) = self.destination() {
361            write_match_rule_string_component(f, "destination", destination, &mut first_component)?;
362        }
363        if let Some(path_spec) = self.path_spec() {
364            let (key, value) = match path_spec {
365                MatchRulePathSpec::Path(path) => ("path", path),
366                MatchRulePathSpec::PathNamespace(ns) => ("path_namespace", ns),
367            };
368            write_match_rule_string_component(f, key, value, &mut first_component)?;
369        }
370        for (i, arg) in self.args() {
371            write_comma(f, &mut first_component)?;
372            write!(f, "arg{i}='{arg}'")?;
373        }
374        for (i, arg_path) in self.arg_paths() {
375            write_comma(f, &mut first_component)?;
376            write!(f, "arg{i}path='{arg_path}'")?;
377        }
378        if let Some(arg0namespace) = self.arg0ns() {
379            write_comma(f, &mut first_component)?;
380            write!(f, "arg0namespace='{arg0namespace}'")?;
381        }
382
383        Ok(())
384    }
385}
386
387fn write_match_rule_string_component(
388    f: &mut std::fmt::Formatter<'_>,
389    key: &str,
390    value: &str,
391    first_component: &mut bool,
392) -> std::fmt::Result {
393    write_comma(f, first_component)?;
394    f.write_str(key)?;
395    f.write_str("='")?;
396    f.write_str(value)?;
397    f.write_char('\'')?;
398
399    Ok(())
400}
401
402fn write_comma(f: &mut std::fmt::Formatter<'_>, first_component: &mut bool) -> std::fmt::Result {
403    if *first_component {
404        *first_component = false;
405    } else {
406        f.write_char(',')?;
407    }
408
409    Ok(())
410}
411
412impl<'m> TryFrom<&'m str> for MatchRule<'m> {
413    type Error = Error;
414
415    fn try_from(s: &'m str) -> Result<Self> {
416        let components = s.split(',');
417        if components.clone().peekable().peek().is_none() {
418            return Err(Error::InvalidMatchRule);
419        }
420        let mut builder = MatchRule::builder();
421        for component in components {
422            let (key, value) = component.split_once('=').ok_or(Error::InvalidMatchRule)?;
423            if key.is_empty()
424                || value.len() < 2
425                || !value.starts_with('\'')
426                || !value.ends_with('\'')
427            {
428                return Err(Error::InvalidMatchRule);
429            }
430            let value = &value[1..value.len() - 1];
431            builder = match key {
432                "type" => {
433                    let msg_type = match value {
434                        "error" => MessageType::Error,
435                        "method_call" => MessageType::MethodCall,
436                        "method_return" => MessageType::MethodReturn,
437                        "signal" => MessageType::Signal,
438                        _ => return Err(Error::InvalidMatchRule),
439                    };
440                    builder.msg_type(msg_type)
441                }
442                "sender" => builder.sender(value)?,
443                "interface" => builder.interface(value)?,
444                "member" => builder.member(value)?,
445                "path" => builder.path(value)?,
446                "path_namespace" => builder.path_namespace(value)?,
447                "destination" => builder.destination(value)?,
448                "arg0namespace" => builder.arg0ns(value)?,
449                key if key.starts_with("arg") => {
450                    if let Some(trailing_idx) = key.find("path") {
451                        let idx = key[3..trailing_idx]
452                            .parse::<u8>()
453                            .map_err(|_| Error::InvalidMatchRule)?;
454                        builder.arg_path(idx, value)?
455                    } else {
456                        let idx = key[3..]
457                            .parse::<u8>()
458                            .map_err(|_| Error::InvalidMatchRule)?;
459                        builder.arg(idx, value)?
460                    }
461                }
462                _ => return Err(Error::InvalidMatchRule),
463            };
464        }
465
466        Ok(builder.build())
467    }
468}
469
470impl<'de: 'm, 'm> Deserialize<'de> for MatchRule<'m> {
471    fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
472    where
473        D: serde::Deserializer<'de>,
474    {
475        let name = <&str>::deserialize(deserializer)?;
476
477        Self::try_from(name).map_err(|e| de::Error::custom(e.to_string()))
478    }
479}
480
481impl Serialize for MatchRule<'_> {
482    fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
483    where
484        S: serde::Serializer,
485    {
486        serializer.serialize_str(&self.to_string())
487    }
488}
489
490/// The path or path namespace.
491#[derive(Clone, Debug, PartialEq, Eq, Hash)]
492pub enum MatchRulePathSpec<'m> {
493    Path(ObjectPath<'m>),
494    PathNamespace(ObjectPath<'m>),
495}
496
497assert_impl_all!(MatchRulePathSpec<'_>: Send, Sync, Unpin);
498
499impl<'m> MatchRulePathSpec<'m> {
500    /// Creates an owned clone of `self`.
501    fn to_owned(&self) -> MatchRulePathSpec<'static> {
502        match self {
503            MatchRulePathSpec::Path(path) => MatchRulePathSpec::Path(path.to_owned()),
504            MatchRulePathSpec::PathNamespace(ns) => MatchRulePathSpec::PathNamespace(ns.to_owned()),
505        }
506    }
507
508    /// Creates an owned clone of `self`.
509    pub fn into_owned(self) -> MatchRulePathSpec<'static> {
510        match self {
511            MatchRulePathSpec::Path(path) => MatchRulePathSpec::Path(path.into_owned()),
512            MatchRulePathSpec::PathNamespace(ns) => {
513                MatchRulePathSpec::PathNamespace(ns.into_owned())
514            }
515        }
516    }
517}
518
519/// Owned sibling of [`MatchRule`].
520#[derive(Clone, Debug, Hash, PartialEq, Eq, Serialize, Type)]
521pub struct OwnedMatchRule(#[serde(borrow)] MatchRule<'static>);
522
523assert_impl_all!(OwnedMatchRule: Send, Sync, Unpin);
524
525impl OwnedMatchRule {
526    /// Convert to the inner `MatchRule`, consuming `self`.
527    pub fn into_inner(self) -> MatchRule<'static> {
528        self.0
529    }
530
531    /// Get a reference to the inner `MatchRule`.
532    pub fn inner(&self) -> &MatchRule<'static> {
533        &self.0
534    }
535}
536
537impl Deref for OwnedMatchRule {
538    type Target = MatchRule<'static>;
539
540    fn deref(&self) -> &Self::Target {
541        &self.0
542    }
543}
544
545impl From<OwnedMatchRule> for MatchRule<'static> {
546    fn from(o: OwnedMatchRule) -> Self {
547        o.into_inner()
548    }
549}
550
551impl<'unowned, 'owned: 'unowned> From<&'owned OwnedMatchRule> for MatchRule<'unowned> {
552    fn from(rule: &'owned OwnedMatchRule) -> Self {
553        rule.inner().clone()
554    }
555}
556
557impl From<MatchRule<'_>> for OwnedMatchRule {
558    fn from(rule: MatchRule<'_>) -> Self {
559        OwnedMatchRule(rule.into_owned())
560    }
561}
562
563impl TryFrom<&'_ str> for OwnedMatchRule {
564    type Error = Error;
565
566    fn try_from(value: &str) -> Result<Self> {
567        Ok(Self::from(MatchRule::try_from(value)?))
568    }
569}
570
571impl<'de> Deserialize<'de> for OwnedMatchRule {
572    fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
573    where
574        D: de::Deserializer<'de>,
575    {
576        String::deserialize(deserializer)
577            .and_then(|r| {
578                MatchRule::try_from(r.as_str())
579                    .map(|r| r.to_owned())
580                    .map_err(|e| de::Error::custom(e.to_string()))
581            })
582            .map(Self)
583    }
584}
585
586impl PartialEq<MatchRule<'_>> for OwnedMatchRule {
587    fn eq(&self, other: &MatchRule<'_>) -> bool {
588        self.0 == *other
589    }
590}