boa_engine/object/builtins/jsregexp.rs
1//! A Rust API wrapper for Boa's `RegExp` Builtin ECMAScript Object
2use crate::{
3 Context, JsExpect, JsNativeError, JsResult, JsValue,
4 builtins::RegExp,
5 error::PanicError,
6 object::{JsArray, JsObject},
7 value::TryFromJs,
8};
9
10use boa_gc::{Finalize, Trace};
11use std::ops::Deref;
12
13/// `JsRegExp` provides a wrapper for Boa's implementation of the ECMAScript `RegExp` builtin object
14///
15/// # Examples
16///
17/// Create a `JsRegExp` and run RegExp.prototype.test( String )
18///
19/// ```
20/// # use boa_engine::{
21/// # object::builtins::JsRegExp,
22/// # Context, JsValue, JsResult,js_string
23/// # };
24/// # fn main() -> JsResult<()> {
25/// // Initialize the `Context`
26/// let context = &mut Context::default();
27///
28/// // Create a new RegExp with pattern and flags
29/// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
30///
31/// let test_result = regexp.test(js_string!("football"), context)?;
32/// assert!(test_result);
33///
34/// let to_string = regexp.to_string(context)?;
35/// assert_eq!(to_string, String::from("/foo/gi"));
36/// # Ok(())
37/// # }
38/// ```
39#[derive(Debug, Clone, Trace, Finalize)]
40pub struct JsRegExp {
41 inner: JsObject,
42}
43
44impl JsRegExp {
45 /// Create a new `JsRegExp` object
46 /// ```
47 /// # use boa_engine::{
48 /// # object::builtins::JsRegExp,
49 /// # Context, JsValue, JsResult, js_string
50 /// # };
51 /// # fn main() -> JsResult<()> {
52 /// // Initialize the `Context`
53 /// let context = &mut Context::default();
54 ///
55 /// // Create a new RegExp with pattern and flags
56 /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
57 /// # Ok(())
58 /// # }
59 /// ```
60 pub fn new<S>(pattern: S, flags: S, context: &mut Context) -> JsResult<Self>
61 where
62 S: Into<JsValue>,
63 {
64 let regexp = RegExp::initialize(None, &pattern.into(), &flags.into(), context)?
65 .as_object()
66 .js_expect("RegExp::initialize must return a RegExp object")?
67 .clone();
68
69 Ok(Self { inner: regexp })
70 }
71
72 /// Create a `JsRegExp` from a regular expression `JsObject`
73 #[inline]
74 pub fn from_object(object: JsObject) -> JsResult<Self> {
75 if object.is::<RegExp>() {
76 Ok(Self { inner: object })
77 } else {
78 Err(JsNativeError::typ()
79 .with_message("object is not a RegExp")
80 .into())
81 }
82 }
83
84 /// Returns a boolean value for whether the `d` flag is present in `JsRegExp` flags
85 #[inline]
86 pub fn has_indices(&self, context: &mut Context) -> JsResult<bool> {
87 RegExp::get_has_indices(&self.inner.clone().into(), &[], context).and_then(|v| {
88 v.as_boolean()
89 .js_expect("value must be a bool")
90 .map_err(Into::into)
91 })
92 }
93
94 /// Returns a boolean value for whether the `g` flag is present in `JsRegExp` flags
95 #[inline]
96 pub fn global(&self, context: &mut Context) -> JsResult<bool> {
97 RegExp::get_global(&self.inner.clone().into(), &[], context).and_then(|v| {
98 v.as_boolean()
99 .js_expect("value must be a bool")
100 .map_err(Into::into)
101 })
102 }
103
104 /// Returns a boolean value for whether the `i` flag is present in `JsRegExp` flags
105 #[inline]
106 pub fn ignore_case(&self, context: &mut Context) -> JsResult<bool> {
107 RegExp::get_ignore_case(&self.inner.clone().into(), &[], context).and_then(|v| {
108 v.as_boolean()
109 .js_expect("value must be a bool")
110 .map_err(Into::into)
111 })
112 }
113
114 /// Returns a boolean value for whether the `m` flag is present in `JsRegExp` flags
115 #[inline]
116 pub fn multiline(&self, context: &mut Context) -> JsResult<bool> {
117 RegExp::get_multiline(&self.inner.clone().into(), &[], context).and_then(|v| {
118 v.as_boolean()
119 .js_expect("value must be a bool")
120 .map_err(Into::into)
121 })
122 }
123
124 /// Returns a boolean value for whether the `s` flag is present in `JsRegExp` flags
125 #[inline]
126 pub fn dot_all(&self, context: &mut Context) -> JsResult<bool> {
127 RegExp::get_dot_all(&self.inner.clone().into(), &[], context).and_then(|v| {
128 v.as_boolean()
129 .js_expect("value must be a bool")
130 .map_err(Into::into)
131 })
132 }
133
134 /// Returns a boolean value for whether the `u` flag is present in `JsRegExp` flags
135 #[inline]
136 pub fn unicode(&self, context: &mut Context) -> JsResult<bool> {
137 RegExp::get_unicode(&self.inner.clone().into(), &[], context).and_then(|v| {
138 v.as_boolean()
139 .js_expect("value must be a bool")
140 .map_err(Into::into)
141 })
142 }
143
144 /// Returns a boolean value for whether the `y` flag is present in `JsRegExp` flags
145 #[inline]
146 pub fn sticky(&self, context: &mut Context) -> JsResult<bool> {
147 RegExp::get_sticky(&self.inner.clone().into(), &[], context).and_then(|v| {
148 v.as_boolean()
149 .js_expect("value must be a bool")
150 .map_err(Into::into)
151 })
152 }
153
154 /// Returns the flags of `JsRegExp` as a string
155 /// ```
156 /// # use boa_engine::{
157 /// # object::builtins::JsRegExp,
158 /// # Context, JsValue, JsResult, js_string
159 /// # };
160 /// # fn main() -> JsResult<()> {
161 /// # let context = &mut Context::default();
162 /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
163 ///
164 /// let flags = regexp.flags(context)?;
165 /// assert_eq!(flags, String::from("gi"));
166 /// # Ok(())
167 /// # }
168 /// ```
169 #[inline]
170 pub fn flags(&self, context: &mut Context) -> JsResult<String> {
171 RegExp::get_flags(&self.inner.clone().into(), &[], context).and_then(|v| {
172 v.as_string()
173 .js_expect("value must be string")?
174 .to_std_string()
175 .map_err(|e| PanicError::new(e.to_string()).into())
176 })
177 }
178
179 /// Returns the source pattern of `JsRegExp` as a string
180 /// ```
181 /// # use boa_engine::{
182 /// # object::builtins::JsRegExp,
183 /// # Context, JsValue, JsResult, js_string
184 /// # };
185 /// # fn main() -> JsResult<()> {
186 /// # let context = &mut Context::default();
187 /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
188 ///
189 /// let src = regexp.source(context)?;
190 /// assert_eq!(src, String::from("foo"));
191 /// # Ok(())
192 /// # }
193 /// ```
194 #[inline]
195 pub fn source(&self, context: &mut Context) -> JsResult<String> {
196 RegExp::get_source(&self.inner.clone().into(), &[], context).and_then(|v| {
197 v.as_string()
198 .js_expect("value must be string")?
199 .to_std_string()
200 .map_err(|e| PanicError::new(e.to_string()).into())
201 })
202 }
203
204 /// Executes a search for a match between `JsRegExp` and the provided string
205 /// ```
206 /// # use boa_engine::{
207 /// # object::builtins::JsRegExp,
208 /// # Context, JsValue, JsResult, js_string
209 /// # };
210 /// # fn main() -> JsResult<()> {
211 /// # let context = &mut Context::default();
212 /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
213 ///
214 /// let test_result = regexp.test(js_string!("football"), context)?;
215 /// assert!(test_result);
216 /// # Ok(())
217 /// # }
218 /// ```
219 pub fn test<S>(&self, search_string: S, context: &mut Context) -> JsResult<bool>
220 where
221 S: Into<JsValue>,
222 {
223 RegExp::test(&self.inner.clone().into(), &[search_string.into()], context).and_then(|v| {
224 v.as_boolean()
225 .js_expect("value must be a bool")
226 .map_err(Into::into)
227 })
228 }
229
230 /// Executes a search for a match in a specified string
231 ///
232 /// Returns a `JsArray` containing matched value and updates the `lastIndex` property, or `None`
233 pub fn exec<S>(&self, search_string: S, context: &mut Context) -> JsResult<Option<JsArray>>
234 where
235 S: Into<JsValue>,
236 {
237 RegExp::exec(&self.inner.clone().into(), &[search_string.into()], context).and_then(|v| {
238 if v.is_null() {
239 Ok(None)
240 } else {
241 let obj = v.to_object(context).js_expect("value must be an array")?;
242 let array = JsArray::from_object(obj)
243 .js_expect("from_object must not fail if value is an array object")?;
244 Ok(Some(array))
245 }
246 })
247 }
248
249 /// Return a string representing the regular expression.
250 /// ```
251 /// # use boa_engine::{
252 /// # object::builtins::JsRegExp,
253 /// # Context, JsValue, JsResult, js_string
254 /// # };
255 /// # fn main() -> JsResult<()> {
256 /// # let context = &mut Context::default();
257 /// let regexp = JsRegExp::new(js_string!("foo"), js_string!("gi"), context)?;
258 ///
259 /// let to_string = regexp.to_string(context)?;
260 /// assert_eq!(to_string, "/foo/gi");
261 /// # Ok(())
262 /// # }
263 /// ```
264 #[inline]
265 pub fn to_string(&self, context: &mut Context) -> JsResult<String> {
266 RegExp::to_string(&self.inner.clone().into(), &[], context).and_then(|v| {
267 v.as_string()
268 .js_expect("value must be a string")?
269 .to_std_string()
270 .map_err(|e| PanicError::new(e.to_string()).into())
271 })
272 }
273}
274
275impl From<JsRegExp> for JsObject {
276 #[inline]
277 fn from(o: JsRegExp) -> Self {
278 o.inner.clone()
279 }
280}
281
282impl From<JsRegExp> for JsValue {
283 #[inline]
284 fn from(o: JsRegExp) -> Self {
285 o.inner.clone().into()
286 }
287}
288
289impl Deref for JsRegExp {
290 type Target = JsObject;
291
292 #[inline]
293 fn deref(&self) -> &Self::Target {
294 &self.inner
295 }
296}
297
298impl TryFromJs for JsRegExp {
299 fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
300 if let Some(o) = value.as_object() {
301 Self::from_object(o.clone())
302 } else {
303 Err(JsNativeError::typ()
304 .with_message("value is not a RegExp object")
305 .into())
306 }
307 }
308}