Skip to main content

tuwunel_service/appservice/
namespace_regex.rs

1use regex::{RegexSet, RegexSetBuilder};
2use ruma::api::appservice::Namespace;
3use tuwunel_core::Result;
4
5/// Compiled regular expressions for a namespace
6#[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	/// Checks if this namespace has rights to a namespace
52	#[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	/// Checks if this namespace has exclusive rights to a namespace
63	#[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}