async_rustbus/
routing.rs

1use std::cmp::Ordering as COrdering;
2use std::collections::hash_map::Iter;
3use std::collections::HashMap;
4use std::fmt::Write;
5use std::fmt::{Debug, Formatter};
6use std::mem::MaybeUninit;
7use std::sync::atomic::{AtomicU8, Ordering};
8use std::sync::Arc;
9
10use std::path::Path;
11
12use super::rustbus_core;
13use super::MsgQueue;
14use rustbus_core::message_builder::{MarshalledMessage, MessageType};
15use rustbus_core::path::ObjectPath;
16
17static mut MAP_TUPLE: (AtomicU8, MaybeUninit<HashMap<String, CallHierarchy>>) =
18    (AtomicU8::new(0), MaybeUninit::uninit());
19
20unsafe fn init_empty_map(flag: u8) -> &'static HashMap<String, CallHierarchy> {
21    if flag == 0
22        && MAP_TUPLE
23            .0
24            .compare_exchange(0, 1, Ordering::AcqRel, Ordering::Relaxed)
25            .is_ok()
26    {
27        MAP_TUPLE.1 = MaybeUninit::new(HashMap::new());
28        MAP_TUPLE.0.store(2, Ordering::Release);
29        return &*MAP_TUPLE.1.as_ptr();
30    }
31    while MAP_TUPLE.0.load(Ordering::Acquire) != 2 {
32        std::hint::spin_loop();
33    }
34    &*MAP_TUPLE.1.as_ptr()
35}
36fn get_empty_map() -> &'static HashMap<String, CallHierarchy> {
37    unsafe {
38        let flag = MAP_TUPLE.0.load(Ordering::Acquire);
39        if flag == 2 {
40            return &*MAP_TUPLE.1.as_ptr();
41        }
42        init_empty_map(flag)
43    }
44}
45
46enum CallHandler {
47    Queue(MsgQueue),
48    Exact(MsgQueue),
49    Intro,
50    Nothing,
51    Drop,
52}
53impl Debug for CallHandler {
54    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
55        match self {
56            CallHandler::Exact(_) => write!(f, "CallHandler::Exact"),
57            CallHandler::Queue(_) => write!(f, "CallHandler::Queue"),
58            CallHandler::Nothing => write!(f, "CallHandler::Nothing"),
59            CallHandler::Drop => write!(f, "CallHandler::Drop"),
60            CallHandler::Intro => write!(f, "CallHandler::Intro"),
61        }
62    }
63}
64impl CallHandler {
65    fn is_nothing(&self) -> bool {
66        matches!(self, CallHandler::Nothing)
67    }
68    fn get_queue(&self) -> Option<&MsgQueue> {
69        match self {
70            CallHandler::Queue(q) | CallHandler::Exact(q) => Some(q),
71            _ => None,
72        }
73    }
74}
75impl From<CallAction> for CallHandler {
76    fn from(action: CallAction) -> Self {
77        match action {
78            CallAction::Queue => CallHandler::Queue(MsgQueue::new()),
79            CallAction::Exact => CallHandler::Exact(MsgQueue::new()),
80            CallAction::Drop => CallHandler::Drop,
81            CallAction::Nothing => CallHandler::Nothing,
82            CallAction::Intro => CallHandler::Intro,
83        }
84    }
85}
86#[derive(Debug)]
87pub(crate) struct CallHierarchy {
88    children: HashMap<String, CallHierarchy>,
89    handler: CallHandler,
90}
91enum Status<'a> {
92    Queue(&'a MsgQueue),
93    Intro(Iter<'a, String, CallHierarchy>),
94    Dropped,
95    Unhandled(Iter<'a, String, CallHierarchy>),
96}
97/// For use with [`RpcConn::insert_call_path`], this enum determines what should be done when receiving incoming method calls.
98///
99///[`RpcConn::insert_call_path`]: ./struct.RpcConn.html#method.insert_call_path
100#[derive(Debug, Clone, Copy, PartialEq, Eq)]
101pub enum CallAction {
102    /// This action causes incoming calls to be dropped
103    Drop,
104    /// This action causes incoming calls within the namespace to be stored, allowing them to be retreived later.
105    Queue,
106    /// This action is the same as `Queue` but requires that call object path is an exact match, rather than also accepting child paths.
107    Exact,
108    /// This action process Introspect calls for this path or children, allowing for clients to discover the objects paths provided by this connection.
109    /// Any other calls received by this action will be replied to with an error.
110    Intro,
111    /// This action does nothing.
112    /// The message is passed on to the parent to be handled by its action.
113    /// This variant is primarily constructed by end users to nullify previously added actions.
114    Nothing,
115}
116impl From<&CallHandler> for CallAction {
117    fn from(handler: &CallHandler) -> Self {
118        match handler {
119            CallHandler::Exact(_) => CallAction::Exact,
120            CallHandler::Queue(_) => CallAction::Queue,
121            CallHandler::Drop => CallAction::Drop,
122            CallHandler::Nothing => CallAction::Nothing,
123            CallHandler::Intro => CallAction::Intro,
124        }
125    }
126}
127impl CallHierarchy {
128    pub fn new() -> Self {
129        CallHierarchy {
130            handler: CallHandler::Drop,
131            children: HashMap::new(),
132        }
133    }
134    pub fn send(&self, msg: MarshalledMessage) -> Result<(), MarshalledMessage> {
135        let path = ObjectPath::from_str(msg.dynheader.object.as_ref().unwrap()).unwrap();
136        let tar_comps = path.components();
137        match self.send_inner(tar_comps) {
138            Status::Queue(queue) => {
139                queue.send(msg);
140                Ok(())
141            }
142            Status::Intro(keys) => Err(make_intro_msg(msg, keys)),
143            Status::Unhandled(_) | Status::Dropped => Err(make_object_not_found(msg)),
144        }
145    }
146    fn send_inner<'a>(&self, mut tar_comps: impl Iterator<Item = &'a str>) -> Status {
147        match tar_comps.next() {
148            Some(child) => match self.children.get(child) {
149                Some(child) => match child.send_inner(tar_comps) {
150                    Status::Unhandled(keys) => match &self.handler {
151                        CallHandler::Nothing => Status::Unhandled(keys),
152                        CallHandler::Queue(q) => Status::Queue(q),
153                        CallHandler::Intro => Status::Intro(keys),
154                        CallHandler::Exact(_) | CallHandler::Drop => Status::Dropped,
155                    },
156                    handled => handled,
157                },
158                None => match &self.handler {
159                    CallHandler::Queue(q) => Status::Queue(q),
160                    CallHandler::Nothing => Status::Unhandled(get_empty_map().iter()),
161                    CallHandler::Intro | CallHandler::Exact(_) | CallHandler::Drop => {
162                        Status::Dropped
163                    }
164                },
165            },
166            None => match &self.handler {
167                CallHandler::Queue(q) | CallHandler::Exact(q) => Status::Queue(q),
168                CallHandler::Nothing => Status::Unhandled(self.children.iter()),
169                CallHandler::Drop => Status::Dropped,
170                CallHandler::Intro => Status::Intro(self.children.iter()),
171            },
172        }
173    }
174    fn insert_inner<'a>(
175        &mut self,
176        mut tar_comps: impl Iterator<Item = &'a str>,
177        action: CallAction,
178    ) -> bool {
179        match tar_comps.next() {
180            Some(child) => match self.children.get_mut(child) {
181                Some(entry) => {
182                    if entry.insert_inner(tar_comps, action) {
183                        true
184                    } else {
185                        self.children.remove(child);
186                        !(self.children.is_empty() && self.handler.is_nothing())
187                    }
188                }
189                None => {
190                    let mut hierarchy = CallHierarchy::new();
191                    hierarchy.handler = CallHandler::Nothing;
192                    if hierarchy.insert_inner(tar_comps, action) {
193                        self.children.insert(child.to_string(), hierarchy);
194                        //eprintln!("insert_inner(): self: {:#?}", self);
195                        true
196                    } else {
197                        !matches!(self.handler, CallHandler::Nothing)
198                    }
199                }
200            },
201            None => {
202                self.handler = action.into();
203                //eprintln!("insert_inner(): self: {:#?}", self);
204                if self.handler.is_nothing() {
205                    !self.children.is_empty()
206                } else {
207                    true
208                }
209            }
210        }
211    }
212    pub fn insert_path(&mut self, path: &ObjectPath, handler: CallAction) {
213        let tar_comps = path.components();
214        self.insert_inner(tar_comps, handler);
215    }
216    fn find_inner<'a>(&self, mut tar_comps: impl Iterator<Item = &'a str>) -> Option<&CallHandler> {
217        match tar_comps.next() {
218            Some(child) => self.children.get(child)?.find_inner(tar_comps),
219            None => Some(&self.handler),
220        }
221    }
222    fn find_handler(&self, path: &ObjectPath) -> Option<&CallHandler> {
223        let tar_comps = path.components();
224        self.find_inner(tar_comps)
225    }
226    pub fn get_queue(&self, path: &ObjectPath) -> Option<&MsgQueue> {
227        let handler = self.find_handler(path)?;
228        handler.get_queue()
229    }
230    pub fn get_action(&self, path: &ObjectPath) -> Option<CallAction> {
231        let handler = self.find_handler(path)?;
232        Some(handler.into())
233    }
234    fn is_match_inner<'a>(
235        &self,
236        mut org_comps: impl Iterator<Item = &'a str>,
237        mut msg_comps: impl Iterator<Item = &'a str>,
238    ) -> bool {
239        match msg_comps.next() {
240            Some(msg) => match org_comps.next() {
241                Some(org) => {
242                    if org == msg {
243                        match self.children.get(org) {
244                            Some(child) => child.is_match_inner(org_comps, msg_comps),
245                            None => false,
246                        }
247                    } else {
248                        false
249                    }
250                }
251                None => match self.children.get(msg) {
252                    Some(child) => match child.send_inner(msg_comps) {
253                        Status::Queue(_) | Status::Dropped | Status::Intro(_) => false,
254                        Status::Unhandled(_) => self.handler.get_queue().is_some(),
255                    },
256                    None => matches!(self.handler, CallHandler::Queue(_)),
257                },
258            },
259            None => match org_comps.next() {
260                Some(_) => false,
261                None => self.handler.get_queue().is_some(),
262            },
263        }
264    }
265    pub fn is_match(&self, org_path: &ObjectPath, msg_path: &ObjectPath) -> bool {
266        let org_comps = org_path.components();
267        let msg_comps = msg_path.components();
268        self.is_match_inner(org_comps, msg_comps)
269    }
270}
271
272fn make_object_not_found(msg: MarshalledMessage) -> MarshalledMessage {
273    msg.dynheader
274        .make_error_response("org.freedesktop.DBus.Error.UnknownObject", None)
275}
276
277const INTRO_START: &str = "<!DOCTYPE node PUBLIC \"-//freedesktop//DTD D-BUS Object Introspection 1.0//EN\" \"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd\">
278 <node>
279\t<interface name=\"org.freedesktop.DBus.Introspectable\">
280\t\t<method name=\"Introspect\">
281\t\t\t<arg name=\"xml_data\" type=\"s\" direction=\"out\"/>
282\t\t</method>
283\t</interface>\n";
284const INTRO_END: &str = " </node>";
285
286fn make_intro_msg(
287    msg: MarshalledMessage,
288    children: Iter<String, CallHierarchy>,
289) -> MarshalledMessage {
290    if msg.dynheader.interface.as_ref().unwrap() == "org.freedesktop.DBus.Introspectable" {
291        let mut res = msg.dynheader.make_response();
292        let mut intro_str = String::from(INTRO_START);
293        let children = children.filter_map(|(s, c)| match c.handler {
294            CallHandler::Drop => None,
295            _ => Some(s),
296        });
297        for child in children {
298            writeln!(intro_str, "\t<node name=\"{}\"/>", child).unwrap();
299        }
300        intro_str.push_str(INTRO_END);
301        res.body.push_param(intro_str).unwrap();
302        res
303    } else {
304        msg.dynheader.make_error_response("UnknownInterface", None)
305    }
306}
307
308#[derive(Default)]
309/// Represents a match for incoming signals.
310///
311/// Signals match a `MatchRule` if they match every field.
312/// When one of the fields is `None` it is equivelent to a wildcard for that field,
313/// causing that field to be matching for every signal.
314///
315/// MatchRule's are ordered by their specificity.
316/// If one `MatchRule` is 'less than' another, then it is more specific than the other one.
317/// See the `Ord` [impl] for details.
318///
319/// [impl]: ./struct.MatchRule.html#impl-Ord
320pub struct MatchRule {
321    /// Checks against the sender of the signal.
322    pub sender: Option<Arc<str>>,
323    /// Matches against the object path of the signal requiring an exact match (no children).
324    /// `path` and `path_namespace` cannot be used simultanously.
325    pub path: Option<Arc<str>>,
326    /// Matches against the object path of the signal.
327    /// It accepts an exact match, or a child of `path_namespace`.
328    /// `path` and `path_namespace` cannot be used simultanously.
329    pub path_namespace: Option<Arc<str>>,
330    /// Matches against the interface of the signal.
331    pub interface: Option<Arc<str>>,
332    /// Matches against the signal member.
333    pub member: Option<Arc<str>>,
334    pub(super) queue: Option<MsgQueue>,
335}
336
337/// A match that accepts every signal.
338/// Every field is None. This is also the `Default` `MatchRule`.
339pub const EMPTY_MATCH: &MatchRule = &MatchRule {
340    sender: None,
341    path: None,
342    path_namespace: None,
343    interface: None,
344    member: None,
345    queue: None,
346};
347impl Debug for MatchRule {
348    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
349        let mut ds = f.debug_struct("MatchRule");
350        ds.field("sender", &self.sender);
351        ds.field("path", &self.path);
352        ds.field("path_namespace", &self.path_namespace);
353        ds.field("interface", &self.interface);
354        ds.field("member", &self.member);
355        struct EmptyPrintable;
356        impl Debug for EmptyPrintable {
357            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
358                write!(f, "_")
359            }
360        }
361        ds.field("queue", &self.queue.as_ref().map(|_| EmptyPrintable));
362        ds.finish()
363    }
364}
365impl Clone for MatchRule {
366    fn clone(&self) -> Self {
367        Self {
368            sender: self.sender.clone(),
369            path: self.path.clone(),
370            path_namespace: self.path_namespace.clone(),
371            interface: self.interface.clone(),
372            member: self.member.clone(),
373            queue: None,
374        }
375    }
376}
377impl MatchRule {
378    pub fn new() -> Self {
379        Self::default()
380    }
381    pub fn sender<S: Into<String>>(&mut self, sender: S) -> &mut Self {
382        self.sender = Some(sender.into().into());
383        self
384    }
385    pub fn path<S: Into<String>>(&mut self, path: S) -> &mut Self {
386        self.path = Some(path.into().into());
387        self.path_namespace = None; // path_namespace is not allowed with path;
388        self
389    }
390    pub fn path_namespace<S: Into<String>>(&mut self, path_namespace: S) -> &mut Self {
391        self.path_namespace = Some(path_namespace.into().into());
392        self.path = None;
393        self
394    }
395    pub fn interface<S: Into<String>>(&mut self, interface: S) -> &mut Self {
396        self.interface = Some(interface.into().into());
397        self
398    }
399    pub fn member<S: Into<String>>(&mut self, member: S) -> &mut Self {
400        self.member = Some(member.into().into());
401        self
402    }
403    pub fn is_empty(&self) -> bool {
404        EMPTY_MATCH == self
405    }
406    /// Returns `true` if the message is a signal and matches the rule.
407    pub fn matches(&self, msg: &MarshalledMessage) -> bool {
408        if !matches!(msg.typ, MessageType::Signal) {
409            return false;
410        }
411        match (&self.sender, &msg.dynheader.sender) {
412            (Some(ss), Some(ms)) => {
413                if ss.as_ref() != ms {
414                    return false;
415                }
416            }
417            (Some(_), None) => return false,
418            (None, _) => {}
419        }
420        match (&self.path, &msg.dynheader.object) {
421            (Some(ss), Some(ms)) => {
422                if ss.as_ref() != ms {
423                    return false;
424                }
425            }
426            (Some(_), None) => return false,
427            (None, _) => {}
428        }
429        match (&self.path_namespace, &msg.dynheader.object) {
430            (Some(ss), Some(ms)) => {
431                if !Path::new(ms).starts_with(ss.as_ref()) {
432                    return false;
433                }
434            }
435            (Some(_), None) => return false,
436            (None, _) => {}
437        }
438        match (&self.interface, &msg.dynheader.interface) {
439            (Some(ss), Some(ms)) => {
440                if ss.as_ref() != ms {
441                    return false;
442                }
443            }
444            (Some(_), None) => return false,
445            (None, _) => {}
446        }
447        match (&self.member, &msg.dynheader.member) {
448            (Some(ss), Some(ms)) => {
449                if ss.as_ref() != ms {
450                    return false;
451                }
452            }
453            (Some(_), None) => return false,
454            (None, _) => {}
455        }
456        true
457    }
458    /// Returns the `org.freedesktop.DBus.AddMatch` match rule string.
459    pub fn match_string(&self) -> String {
460        let mut match_str = String::new();
461        if let Some(sender) = &self.sender {
462            match_str.push_str("sender='");
463            match_str.push_str(sender);
464            match_str.push_str("',");
465        }
466        if let Some(path) = &self.path {
467            match_str.push_str("path='");
468            match_str.push_str(path);
469            match_str.push_str("',");
470        }
471        if let Some(path_namespace) = &self.path_namespace {
472            match_str.push_str("path_namespace='");
473            match_str.push_str(path_namespace);
474            match_str.push_str("',");
475        }
476        if let Some(interface) = &self.interface {
477            match_str.push_str("interface='");
478            match_str.push_str(interface);
479            match_str.push_str("',");
480        }
481        if let Some(member) = &self.member {
482            match_str.push_str("member='");
483            match_str.push_str(member);
484            match_str.push_str("',");
485        }
486        match_str.push_str("type='signal'");
487        match_str
488    }
489}
490impl PartialEq<MatchRule> for MatchRule {
491    fn eq(&self, other: &MatchRule) -> bool {
492        if self.sender != other.sender {
493            return false;
494        }
495        if self.path != other.path {
496            return false;
497        }
498        if self.path != other.path {
499            return false;
500        }
501        if self.path_namespace != other.path_namespace {
502            return false;
503        }
504        if self.interface != other.interface {
505            return false;
506        }
507        if self.member != other.member {
508            return false;
509        }
510        true
511    }
512}
513impl Eq for MatchRule {}
514fn option_ord<T>(left: &Option<T>, right: &Option<T>) -> Option<COrdering> {
515    match &left {
516        Some(_) => {
517            if right.is_none() {
518                return Some(COrdering::Less);
519            }
520        }
521        None => {
522            if right.is_some() {
523                return Some(COrdering::Greater);
524            }
525        }
526    }
527    None
528}
529fn path_subset(left: &Option<Arc<str>>, right: &Option<Arc<str>>) -> Option<COrdering> {
530    if let Some(ord) = option_ord(left, right) {
531        return Some(ord);
532    }
533    let mut l_path = match &left {
534        Some(p) => Path::new(p.as_ref()).components(),
535        None => return None,
536    };
537    let mut r_path = Path::new(right.as_ref().unwrap().as_ref()).components();
538    loop {
539        break match (l_path.next(), r_path.next()) {
540            (Some(l_comp), Some(r_comp)) => {
541                if l_comp == r_comp {
542                    continue;
543                } else {
544                    None
545                }
546            }
547            (Some(_), None) => Some(COrdering::Less),
548            (None, Some(_)) => Some(COrdering::Greater),
549            (None, None) => None,
550        };
551    }
552}
553/// `MatchRule`s are ordered by their specificity.
554/// If one match rule is 'less than' another then it is more specific than the other.
555/// When evaluating specificity the following steps are taken:
556/// 1. If one rule has `Some` `sender` and the other `None` then, the former is less than the latter.
557/// Otherwise continue to the next step.
558/// 2. If one rule has `Some` `path` and the other `None` then, the former is less than the latter.
559/// Otherwise continue to the next step.
560/// 3. If one rule has `Some` `path_namespace` and the other `None` then, the former is less than the latter.
561/// Otherwise continue to the next step.
562/// 4. If both rules have `Some` `path_namespace` and one is a subset of the other than the former is less than the latter.
563/// Otherwise continue to the next step.
564/// 5. If one rule has `Some` `interface` and the other `None` then, the former is less than the latter.
565/// Otherwise continue to the next step.
566/// 6. If one rule has `Some` `member` and the other `None` then, the former is less than the latter.
567/// Otherwise continue to the next step.
568/// 7. Compare `sender` field.
569/// If not equal return the `Ordering`, otherwise continue to the next step.
570/// 8. Compare `path` field.
571/// If not equal return the `Ordering`, otherwise continue to the next step.
572/// 9. Compare `path_namespace` field.
573/// If not equal return the `Ordering`, otherwise continue to the next step.
574/// 10. Compare `interface` field.
575/// If not equal return the `Ordering`, otherwise continue to the next step.
576/// 11. Compare `member` field, and return the result.
577impl Ord for MatchRule {
578    fn cmp(&self, other: &Self) -> COrdering {
579        /*eprintln!("MatchRule::cmp(\n\
580        self: {:#?},\nother: {:#?})", self, other);*/
581        if let Some(ord) = option_ord(&self.sender, &other.sender) {
582            return ord;
583        }
584        if let Some(ord) = option_ord(&self.path, &other.path) {
585            return ord;
586        }
587        if let Some(ord) = path_subset(&self.path_namespace, &other.path_namespace) {
588            return ord;
589        }
590        if let Some(ord) = option_ord(&self.interface, &other.interface) {
591            return ord;
592        }
593        if let Some(ord) = option_ord(&self.member, &other.member) {
594            return ord;
595        }
596        self.sender
597            .cmp(&other.sender)
598            .then_with(|| self.path.cmp(&other.path))
599            .then_with(|| self.path_namespace.cmp(&other.path_namespace))
600            .then_with(|| self.interface.cmp(&other.interface))
601            .then_with(|| self.member.cmp(&other.member))
602    }
603}
604impl PartialOrd<MatchRule> for MatchRule {
605    fn partial_cmp(&self, other: &MatchRule) -> Option<COrdering> {
606        Some(self.cmp(other))
607    }
608}
609
610pub fn queue_sig(sig_matches: &[MatchRule], sig: MarshalledMessage) {
611    for sig_match in sig_matches {
612        if sig_match.matches(&sig) {
613            sig_match.queue.as_ref().unwrap().send(sig);
614            return;
615        }
616    }
617}
618
619#[cfg(test)]
620mod tests {
621    use super::rustbus_core;
622    use super::{CallAction, CallHierarchy, MatchRule, MsgQueue, EMPTY_MATCH};
623    use std::convert::TryInto;
624
625    #[test]
626    fn call_hierarchy_insert() {
627        let mut hierarchy = CallHierarchy::new();
628        hierarchy.insert_path("/usr/local/bin".try_into().unwrap(), CallAction::Queue);
629        assert_eq!(
630            hierarchy
631                .get_action("/usr/local/bin".try_into().unwrap())
632                .unwrap(),
633            CallAction::Queue
634        );
635        assert_eq!(
636            hierarchy
637                .get_action("/usr/local".try_into().unwrap())
638                .unwrap(),
639            CallAction::Nothing
640        );
641        assert_eq!(
642            hierarchy.get_action("/usr".try_into().unwrap()).unwrap(),
643            CallAction::Nothing
644        );
645        assert_eq!(
646            hierarchy.get_action("/".try_into().unwrap()).unwrap(),
647            CallAction::Drop
648        );
649        assert!(hierarchy.is_match(
650            "/usr/local/bin".try_into().unwrap(),
651            "/usr/local/bin/echo".try_into().unwrap()
652        ));
653        assert!(hierarchy.is_match(
654            "/usr/local/bin".try_into().unwrap(),
655            "/usr/local/bin".try_into().unwrap()
656        ));
657        assert!(!hierarchy.is_match(
658            "/usr/local".try_into().unwrap(),
659            "/usr/local".try_into().unwrap()
660        ));
661        assert!(!hierarchy.is_match("/usr".try_into().unwrap(), "/usr/local".try_into().unwrap()));
662        assert!(!hierarchy.is_match(
663            "/".try_into().unwrap(),
664            "/usr/local/bin".try_into().unwrap()
665        ));
666        assert!(!hierarchy.is_match("/".try_into().unwrap(), "/usr/local".try_into().unwrap()));
667        hierarchy.insert_path("/".try_into().unwrap(), CallAction::Queue);
668        assert!(hierarchy.is_match("/".try_into().unwrap(), "/usr/local".try_into().unwrap()));
669        hierarchy.insert_path("/var".try_into().unwrap(), CallAction::Exact);
670        hierarchy.insert_path("/var/log/journal".try_into().unwrap(), CallAction::Queue);
671        assert!(hierarchy.is_match(
672            "/var/log/journal".try_into().unwrap(),
673            "/var/log/journal".try_into().unwrap()
674        ));
675        assert!(hierarchy.is_match("/var".try_into().unwrap(), "/var/log".try_into().unwrap()));
676        assert!(!hierarchy.is_match("/".try_into().unwrap(), "/var/log".try_into().unwrap()));
677        assert!(!hierarchy.is_match("/".try_into().unwrap(), "/var".try_into().unwrap()));
678    }
679    #[test]
680    fn trimming() {
681        let mut hierarchy = CallHierarchy::new();
682        hierarchy.insert_path("/usr/local/bin".try_into().unwrap(), CallAction::Queue);
683        hierarchy.insert_path(
684            "/usr/local/bin/hello/find".try_into().unwrap(),
685            CallAction::Queue,
686        );
687        hierarchy.insert_path(
688            "/usr/local/bin/hello/find".try_into().unwrap(),
689            CallAction::Nothing,
690        );
691        hierarchy.insert_path("/usr/local/bin".try_into().unwrap(), CallAction::Nothing);
692        println!("{:#?}", hierarchy);
693        assert!(hierarchy.children.is_empty());
694        hierarchy.insert_path("/usr/local/bin".try_into().unwrap(), CallAction::Queue);
695        hierarchy.insert_path(
696            "/usr/local/bin/hello/find".try_into().unwrap(),
697            CallAction::Queue,
698        );
699        hierarchy.insert_path("/usr/local/bin".try_into().unwrap(), CallAction::Nothing);
700        hierarchy.insert_path(
701            "/usr/local/bin/hello/find".try_into().unwrap(),
702            CallAction::Nothing,
703        );
704        assert!(hierarchy.children.is_empty());
705    }
706
707    #[test]
708    fn match_order() {
709        use rand::seq::SliceRandom;
710        use rand::thread_rng;
711        let mut w_sender = MatchRule::new();
712        w_sender.sender("org.freedesktop.DBus");
713        let mut w_path = MatchRule::new();
714        w_path.path("/hello");
715        let mut w_namespace0 = MatchRule::new();
716        w_namespace0.path_namespace("/org");
717        let mut w_namespace1 = MatchRule::new();
718        w_namespace1.path_namespace("/org/freedesktop");
719        let mut w_interface = MatchRule::new();
720        w_interface.interface("org.freedesktop.DBus");
721        let mut w_member = MatchRule::new();
722        w_member.member("Peer");
723        let mut array = [
724            &w_sender,
725            &w_path,
726            &w_namespace0,
727            &w_namespace1,
728            &w_interface,
729            &w_member,
730        ];
731        let mut rng = thread_rng();
732        array.shuffle(&mut rng);
733        array.sort_unstable();
734        assert!(std::ptr::eq(&w_sender, array[0]));
735        assert!(std::ptr::eq(&w_path, array[1]));
736        assert!(std::ptr::eq(&w_namespace1, array[2]));
737        assert!(std::ptr::eq(&w_namespace0, array[3]));
738        assert!(std::ptr::eq(&w_interface, array[4]));
739        assert!(std::ptr::eq(&w_member, array[5]));
740    }
741    use rustbus_core::message_builder::MessageBuilder;
742    #[test]
743    fn matches_single() {
744        let m1 = MatchRule::new().interface("io.test.Test1").clone();
745        let mut m1_q = m1.clone();
746        m1_q.queue = Some(MsgQueue::new());
747
748        let m2 = MatchRule::new().member("TestSig1").clone();
749        let mut m2_q = m2.clone();
750        m2_q.queue = Some(MsgQueue::new());
751
752        let mut m3 = m2.clone();
753        m3.interface = m1.interface.clone();
754        let mut m3_q = m3.clone();
755        m3_q.queue = Some(MsgQueue::new());
756
757        let m4 = MatchRule::new().path_namespace("/io/test").clone();
758        let mut m4_q = m4.clone();
759        m4_q.queue = Some(MsgQueue::new());
760
761        let m5 = MatchRule::new().path("/io/test/specific").clone();
762        let mut m5_q = m5.clone();
763        m5_q.queue = Some(MsgQueue::new());
764
765        let m6 = MatchRule::new().sender("io.test_sender").clone();
766        let mut m6_q = m6.clone();
767        m6_q.queue = Some(MsgQueue::new());
768
769        let mut msg = MessageBuilder::new()
770            .signal("io.test.Test1", "TestSig1", "/")
771            .build();
772        msg.dynheader.sender = Some("io.other".into());
773
774        assert!(EMPTY_MATCH.matches(&msg));
775        assert!(m1.matches(&msg));
776        assert!(m1_q.matches(&msg));
777        assert!(m2.matches(&msg));
778        assert!(m2_q.matches(&msg));
779        assert!(m3.matches(&msg));
780        assert!(m3_q.matches(&msg));
781        assert!(!m4.matches(&msg));
782        assert!(!m4_q.matches(&msg));
783        assert!(!m5.matches(&msg));
784        assert!(!m5_q.matches(&msg));
785        assert!(!m6.matches(&msg));
786        assert!(!m6_q.matches(&msg));
787
788        let mut other_if = Some("io.test.Test2".to_string());
789        std::mem::swap(&mut msg.dynheader.interface, &mut other_if);
790        assert!(!m1.matches(&msg));
791        assert!(!m1_q.matches(&msg));
792        assert!(m2.matches(&msg));
793        assert!(m2_q.matches(&msg));
794        assert!(!m3.matches(&msg));
795        assert!(!m3_q.matches(&msg));
796        std::mem::swap(&mut msg.dynheader.interface, &mut other_if);
797
798        msg.dynheader.member = Some("TestSig2".into());
799        assert!(m1.matches(&msg));
800        assert!(m1_q.matches(&msg));
801        assert!(!m2.matches(&msg));
802        assert!(!m2_q.matches(&msg));
803        assert!(!m3.matches(&msg));
804        assert!(!m3_q.matches(&msg));
805
806        msg.dynheader.object = Some("/io/test".into());
807        assert!(m4.matches(&msg));
808        assert!(m4_q.matches(&msg));
809        assert!(!m5.matches(&msg));
810        assert!(!m5_q.matches(&msg));
811
812        msg.dynheader.object = Some("/io/test/specific".into());
813        assert!(m4.matches(&msg));
814        assert!(m4_q.matches(&msg));
815        assert!(m5.matches(&msg));
816        assert!(m5_q.matches(&msg));
817
818        msg.dynheader.object = Some("/io/test/specific/too".into());
819        assert!(m4.matches(&msg));
820        assert!(m4_q.matches(&msg));
821        assert!(!m5.matches(&msg));
822        assert!(!m5_q.matches(&msg));
823
824        msg.dynheader.sender = Some("io.test_sender".into());
825        assert!(m6.matches(&msg));
826        assert!(m6_q.matches(&msg));
827        assert!(EMPTY_MATCH.matches(&msg));
828    }
829    #[test]
830    fn matches_is_empty() {
831        assert!(EMPTY_MATCH.is_empty());
832        let mut me_q = MatchRule::new();
833        me_q.queue = Some(MsgQueue::new());
834        assert!(me_q.is_empty());
835    }
836}