Skip to main content

surrealdb_types/value/
regex.rs

1use std::cmp::Ordering;
2use std::fmt::{self, Debug, Display, Formatter};
3use std::hash::{Hash, Hasher};
4use std::ops::Deref;
5use std::str::FromStr;
6
7use regex::RegexBuilder;
8use serde::de::{self, Visitor};
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10
11use crate::sql::{SqlFormat, ToSql};
12
13pub(crate) const REGEX_TOKEN: &str = "$surrealdb::public::Regex";
14
15/// Represents a regular expression in SurrealDB
16///
17/// A regular expression is a pattern used for matching strings.
18/// This type wraps the `regex::Regex` type and provides custom serialization/deserialization.
19#[derive(Clone)]
20pub struct Regex(pub(crate) ::regex::Regex);
21
22impl Regex {
23	/// Returns a reference to the underlying regex
24	///
25	/// Note: This method returns the regex without the '/' delimiters that are used in display.
26	pub fn regex(&self) -> &regex::Regex {
27		&self.0
28	}
29
30	/// Convert into the inner regex::Regex
31	pub fn into_inner(self) -> regex::Regex {
32		self.0
33	}
34}
35
36impl From<regex::Regex> for Regex {
37	fn from(regex: regex::Regex) -> Self {
38		Regex(regex)
39	}
40}
41
42impl FromStr for Regex {
43	type Err = <regex::Regex as FromStr>::Err;
44
45	fn from_str(s: &str) -> Result<Self, Self::Err> {
46		Ok(Regex(RegexBuilder::new(&s.replace("\\/", "/")).build()?))
47	}
48}
49
50impl PartialEq for Regex {
51	fn eq(&self, other: &Self) -> bool {
52		self.0.as_str() == other.0.as_str()
53	}
54}
55
56impl Eq for Regex {}
57
58impl Ord for Regex {
59	fn cmp(&self, other: &Self) -> Ordering {
60		self.0.as_str().cmp(other.0.as_str())
61	}
62}
63
64impl PartialOrd for Regex {
65	fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
66		Some(self.cmp(other))
67	}
68}
69
70impl Hash for Regex {
71	fn hash<H: Hasher>(&self, state: &mut H) {
72		self.0.as_str().hash(state);
73	}
74}
75
76impl Deref for Regex {
77	type Target = regex::Regex;
78
79	fn deref(&self) -> &Self::Target {
80		&self.0
81	}
82}
83
84impl Debug for Regex {
85	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
86		let t = self.0.to_string().replace('/', "\\/");
87		write!(f, "/{}/", &t)
88	}
89}
90
91impl Display for Regex {
92	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
93		let t = self.0.to_string().replace('/', "\\/");
94		write!(f, "/{}/", &t)
95	}
96}
97
98impl ToSql for Regex {
99	fn fmt_sql(&self, f: &mut String, _fmt: SqlFormat) {
100		f.push('/');
101		f.push_str(&self.0.to_string().replace('/', "\\/"));
102		f.push('/');
103	}
104}
105
106impl Serialize for Regex {
107	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
108	where
109		S: Serializer,
110	{
111		serializer.serialize_newtype_struct(REGEX_TOKEN, self.0.as_str())
112	}
113}
114
115impl<'de> Deserialize<'de> for Regex {
116	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117	where
118		D: Deserializer<'de>,
119	{
120		struct RegexNewtypeVisitor;
121
122		impl<'de> Visitor<'de> for RegexNewtypeVisitor {
123			type Value = Regex;
124
125			fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
126				formatter.write_str("a regex newtype")
127			}
128
129			fn visit_newtype_struct<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
130			where
131				D: Deserializer<'de>,
132			{
133				struct RegexVisitor;
134
135				impl Visitor<'_> for RegexVisitor {
136					type Value = Regex;
137
138					fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
139						formatter.write_str("a regex str")
140					}
141
142					fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
143					where
144						E: de::Error,
145					{
146						regex::Regex::from_str(value)
147							.map(Regex)
148							.map_err(|_| de::Error::custom("invalid regex"))
149					}
150				}
151
152				deserializer.deserialize_str(RegexVisitor)
153			}
154		}
155
156		deserializer.deserialize_newtype_struct(REGEX_TOKEN, RegexNewtypeVisitor)
157	}
158}
159
160#[cfg(feature = "arbitrary")]
161mod arb {
162	use ::arbitrary::Arbitrary;
163
164	use super::*;
165
166	impl<'a> Arbitrary<'a> for Regex {
167		fn arbitrary(u: &mut ::arbitrary::Unstructured<'a>) -> ::arbitrary::Result<Self> {
168			let ast = regex_syntax::ast::Ast::arbitrary(u)?;
169			let src = &ast.to_string();
170			if src.is_empty() {
171				return Err(::arbitrary::Error::IncorrectFormat);
172			}
173			let regex =
174				RegexBuilder::new(src).build().map_err(|_| ::arbitrary::Error::IncorrectFormat)?;
175			Ok(Regex(regex))
176		}
177	}
178}