cord_message/
pattern.rs

1use std::{convert::From, ops::Deref};
2
3#[derive(Clone, Debug, Eq, Hash, PartialEq)]
4pub struct Pattern(String);
5
6impl Pattern {
7    pub fn new<S: Into<String>>(pattern: S) -> Self {
8        Pattern(pattern.into())
9    }
10
11    // The most generic namespace has the greatest value
12    pub fn contains(&self, other: &Pattern) -> bool {
13        if self == other {
14            true
15        } else {
16            // E.g. /a contains /a/b
17            other.0.starts_with(&self.0)
18                && (self.0.len() == 1 || other.0.chars().nth(self.0.len()) == Some('/'))
19        }
20    }
21}
22
23impl Deref for Pattern {
24    type Target = String;
25
26    fn deref(&self) -> &Self::Target {
27        &self.0
28    }
29}
30
31impl<S> From<S> for Pattern
32where
33    S: Into<String>,
34{
35    fn from(s: S) -> Pattern {
36        Pattern::new(s)
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    #[test]
45    fn test_root_namespace() {
46        assert!(Pattern::new("/").contains(&Pattern::new("/a")));
47    }
48
49    #[test]
50    fn test_equality() {
51        assert_eq!(Pattern::new("/a"), Pattern::new("/a"));
52    }
53
54    #[test]
55    fn test_case_inequality() {
56        assert_ne!(Pattern::new("/a"), Pattern::new("/A"));
57    }
58
59    #[test]
60    fn test_inequality() {
61        assert!(!Pattern::new("/a/b").contains(&Pattern::new("/ab")));
62    }
63
64    #[test]
65    fn test_contains_child_namespace() {
66        assert!(Pattern::new("/a").contains(&Pattern::new("/a/b")));
67    }
68
69    #[test]
70    fn test_not_contains_parent() {
71        assert!(!Pattern::new("/a/b").contains(&Pattern::new("/a")));
72    }
73}