surrealdb_types/value/
regex.rs1use 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#[derive(Clone)]
20pub struct Regex(pub(crate) ::regex::Regex);
21
22impl Regex {
23 pub fn regex(&self) -> ®ex::Regex {
27 &self.0
28 }
29
30 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}