Skip to main content

hawdb_core/
regex.rs

1// Copyright 2026 Nowledge
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt::{Debug, Formatter};
16use std::sync::Arc;
17
18use crate::{HawDBError, Result};
19
20#[derive(Clone)]
21pub struct ValidatedRegex {
22    source: String,
23    compiled: Arc<regex::Regex>,
24}
25
26impl ValidatedRegex {
27    pub fn new(source: impl Into<String>) -> Result<Self> {
28        let source = source.into();
29        let compiled = regex::Regex::new(&source)
30            .map_err(|error| HawDBError::Semantic(format!("invalid regex pattern: {error}")))?;
31        Ok(Self {
32            source,
33            compiled: Arc::new(compiled),
34        })
35    }
36
37    pub fn as_str(&self) -> &str {
38        &self.source
39    }
40
41    pub fn is_match(&self, value: &str) -> bool {
42        self.compiled.is_match(value)
43    }
44}
45
46impl Debug for ValidatedRegex {
47    fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
48        formatter
49            .debug_tuple("ValidatedRegex")
50            .field(&self.source)
51            .finish()
52    }
53}
54
55impl PartialEq for ValidatedRegex {
56    fn eq(&self, other: &Self) -> bool {
57        self.source == other.source
58    }
59}
60
61impl Eq for ValidatedRegex {}
62
63#[cfg(test)]
64mod tests {
65    use std::sync::Arc;
66
67    use super::ValidatedRegex;
68
69    #[test]
70    fn clone_reuses_compiled_regex() {
71        let pattern = ValidatedRegex::new("^memory-[0-9]+$").expect("valid regex");
72        let cloned = pattern.clone();
73
74        assert!(Arc::ptr_eq(&pattern.compiled, &cloned.compiled));
75        assert!(cloned.is_match("memory-42"));
76        assert!(!cloned.is_match("thread-42"));
77    }
78
79    #[test]
80    fn equality_and_debug_use_the_source_pattern() {
81        let left = ValidatedRegex::new("memory.*").expect("valid regex");
82        let right = ValidatedRegex::new("memory.*").expect("valid regex");
83
84        assert_eq!(left, right);
85        assert_eq!(left.as_str(), "memory.*");
86        assert_eq!(format!("{left:?}"), "ValidatedRegex(\"memory.*\")");
87    }
88
89    #[test]
90    fn invalid_patterns_fail_during_construction() {
91        assert!(ValidatedRegex::new("[").is_err());
92    }
93}