tuwunel_service/appservice/
namespace_regex.rs1use regex::{RegexSet, RegexSetBuilder};
2use ruma::api::appservice::Namespace;
3use tuwunel_core::Result;
4
5#[derive(Clone, Debug)]
7pub struct NamespaceRegex {
8 pub exclusive: Option<RegexSet>,
9 pub non_exclusive: Option<RegexSet>,
10}
11
12impl NamespaceRegex {
13 pub(super) fn new<'a, I>(case_sensitive: bool, value: I) -> Result<Self, regex::Error>
14 where
15 I: Iterator<Item = &'a Namespace> + Clone + Send,
16 {
17 let exclusive = value
18 .clone()
19 .filter(|namespace| namespace.exclusive)
20 .map(|namespace| namespace.regex.as_str());
21
22 let non_exclusive = value
23 .filter(|namespace| !namespace.exclusive)
24 .map(|namespace| namespace.regex.as_str());
25
26 Ok(Self {
27 exclusive: exclusive
28 .clone()
29 .count()
30 .gt(&0)
31 .then(|| {
32 RegexSetBuilder::new(exclusive)
33 .case_insensitive(!case_sensitive)
34 .build()
35 })
36 .transpose()?,
37
38 non_exclusive: non_exclusive
39 .clone()
40 .count()
41 .gt(&0)
42 .then(|| {
43 RegexSetBuilder::new(non_exclusive)
44 .case_insensitive(!case_sensitive)
45 .build()
46 })
47 .transpose()?,
48 })
49 }
50
51 #[inline]
53 #[must_use]
54 pub fn is_match(&self, input: &str) -> bool {
55 self.is_exclusive_match(input)
56 || self
57 .non_exclusive
58 .as_ref()
59 .is_some_and(|non_exclusive| non_exclusive.is_match(input))
60 }
61
62 #[inline]
64 #[must_use]
65 pub fn is_exclusive_match(&self, input: &str) -> bool {
66 self.exclusive
67 .as_ref()
68 .is_some_and(|exclusive| exclusive.is_match(input))
69 }
70}