zbus/match_rule/
mod.rs

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