surrealdb_core/sql/
regex.rs1use once_cell::sync::Lazy;
2use quick_cache::sync::{Cache, GuardResult};
3use revision::revisioned;
4use serde::{
5 de::{self, Visitor},
6 Deserialize, Deserializer, Serialize, Serializer,
7};
8use std::cmp::Ordering;
9use std::fmt::Debug;
10use std::fmt::{self, Display, Formatter};
11use std::hash::{Hash, Hasher};
12use std::str::FromStr;
13use std::{env, str};
14
15pub(crate) const TOKEN: &str = "$surrealdb::private::sql::Regex";
16
17#[revisioned(revision = 1)]
18#[derive(Clone)]
19#[non_exhaustive]
20pub struct Regex(pub regex::Regex);
21
22impl Regex {
23 pub fn regex(&self) -> ®ex::Regex {
25 &self.0
26 }
27}
28
29fn regex_new(str: &str) -> Result<regex::Regex, regex::Error> {
30 static REGEX_CACHE: Lazy<Cache<String, regex::Regex>> = Lazy::new(|| {
31 let cache_size: usize = env::var("SURREAL_REGEX_CACHE_SIZE")
32 .map_or(1000, |v| v.parse().unwrap_or(1000))
33 .max(10); Cache::new(cache_size)
35 });
36 match REGEX_CACHE.get_value_or_guard(str, None) {
37 GuardResult::Value(v) => Ok(v),
38 GuardResult::Guard(g) => {
39 let re = regex::Regex::new(str)?;
40 g.insert(re.clone()).ok();
41 Ok(re)
42 }
43 GuardResult::Timeout => {
44 warn!("Regex cache timeout");
45 regex::Regex::new(str)
46 }
47 }
48}
49
50impl FromStr for Regex {
51 type Err = <regex::Regex as FromStr>::Err;
52
53 fn from_str(s: &str) -> Result<Self, Self::Err> {
54 if s.contains('\0') {
55 Err(regex::Error::Syntax("regex contained NUL byte".to_owned()))
56 } else {
57 regex_new(&s.replace("\\/", "/")).map(Self)
58 }
59 }
60}
61
62impl PartialEq for Regex {
63 fn eq(&self, other: &Self) -> bool {
64 let str_left = self.0.as_str();
65 let str_right = other.0.as_str();
66 str_left == str_right
67 }
68}
69
70impl Eq for Regex {}
71
72impl Ord for Regex {
73 fn cmp(&self, other: &Self) -> Ordering {
74 self.0.as_str().cmp(other.0.as_str())
75 }
76}
77
78impl PartialOrd for Regex {
79 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
80 Some(self.cmp(other))
81 }
82}
83
84impl Hash for Regex {
85 fn hash<H: Hasher>(&self, state: &mut H) {
86 self.0.as_str().hash(state);
87 }
88}
89
90impl Debug for Regex {
91 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
92 Display::fmt(self, f)
93 }
94}
95
96impl Display for Regex {
97 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
98 write!(f, "/{}/", &self.0)
99 }
100}
101
102impl Serialize for Regex {
103 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
104 where
105 S: Serializer,
106 {
107 serializer.serialize_newtype_struct(TOKEN, self.0.as_str())
108 }
109}
110
111impl<'de> Deserialize<'de> for Regex {
112 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
113 where
114 D: Deserializer<'de>,
115 {
116 struct RegexNewtypeVisitor;
117
118 impl<'de> Visitor<'de> for RegexNewtypeVisitor {
119 type Value = Regex;
120
121 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
122 formatter.write_str("a regex newtype")
123 }
124
125 fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
126 where
127 D: Deserializer<'de>,
128 {
129 struct RegexVisitor;
130
131 impl<'de> Visitor<'de> for RegexVisitor {
132 type Value = Regex;
133
134 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
135 formatter.write_str("a regex str")
136 }
137
138 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
139 where
140 E: de::Error,
141 {
142 Regex::from_str(value).map_err(|_| de::Error::custom("invalid regex"))
143 }
144 }
145
146 deserializer.deserialize_str(RegexVisitor)
147 }
148 }
149
150 deserializer.deserialize_newtype_struct(TOKEN, RegexNewtypeVisitor)
151 }
152}