1use std::ops::{Deref, DerefMut};
8use std::ptr::NonNull;
9
10use mozjs::glue::JS_GetRegExpFlags;
11use mozjs::jsapi::{
12 CheckRegExpSyntax, ExecuteRegExp, ExecuteRegExpNoStatics, GetRegExpSource, JSObject, NewUCRegExpObject,
13 ObjectIsRegExp, RegExpFlags as REFlags,
14};
15use mozjs::rooted;
16
17use crate::flags::RegExpFlags;
18use crate::{Context, Local, Object, Value};
19
20#[derive(Debug)]
21pub struct RegExp<'r> {
22 re: Local<'r, *mut JSObject>,
23}
24
25impl<'r> RegExp<'r> {
26 pub fn new(cx: &'r Context, source: &str, flags: RegExpFlags) -> Option<RegExp<'r>> {
28 let source: Vec<u16> = source.encode_utf16().collect();
29 let regexp = unsafe { NewUCRegExpObject(cx.as_ptr(), source.as_ptr(), source.len(), flags.into()) };
30 NonNull::new(regexp).map(|re| RegExp { re: cx.root(re.as_ptr()) })
31 }
32
33 pub fn from(cx: &Context, object: Local<'r, *mut JSObject>) -> Option<RegExp<'r>> {
36 RegExp::is_regexp(cx, &object).then_some(RegExp { re: object })
37 }
38
39 pub unsafe fn from_unchecked(object: Local<'r, *mut JSObject>) -> RegExp<'r> {
44 RegExp { re: object }
45 }
46
47 pub fn source<'cx>(&self, cx: &'cx Context) -> crate::String<'cx> {
48 crate::String::from(cx.root(unsafe { GetRegExpSource(cx.as_ptr(), self.handle().into()) }))
49 }
50
51 pub fn flags(&self, cx: &Context) -> RegExpFlags {
52 let mut flags = REFlags { flags_: 0 };
53 unsafe {
54 JS_GetRegExpFlags(cx.as_ptr(), self.handle().into(), &raw mut flags);
55 }
56 flags.into()
57 }
58
59 pub fn to_string(&self, cx: &Context) -> crate::Result<String> {
60 Ok(format!("/{}/{}", self.source(cx).to_owned(cx)?, self.flags(cx)))
61 }
62
63 pub fn execute_test(&self, cx: &Context, string: &str, index: &mut usize) -> bool {
64 let mut rval = Value::null(cx);
65 self.execute(cx, string, index, true, &mut rval, true) && rval.handle().to_boolean()
66 }
67
68 pub fn execute_test_no_static(&self, cx: &Context, string: &str, index: &mut usize) -> bool {
69 let mut rval = Value::null(cx);
70 self.execute(cx, string, index, true, &mut rval, false) && rval.handle().to_boolean()
71 }
72
73 pub fn execute_match<'cx>(&self, cx: &'cx Context, string: &str, index: &mut usize) -> Option<Value<'cx>> {
74 let mut rval = Value::null(cx);
75 self.execute(cx, string, index, false, &mut rval, true).then_some(rval)
76 }
77
78 pub fn execute_match_no_static<'cx>(
79 &self, cx: &'cx Context, string: &str, index: &mut usize,
80 ) -> Option<Value<'cx>> {
81 let mut rval = Value::null(cx);
82 self.execute(cx, string, index, false, &mut rval, false).then_some(rval)
83 }
84
85 fn execute<'cx>(
86 &self, cx: &'cx Context, string: &str, index: &mut usize, test: bool, rval: &mut Value<'cx>, with_static: bool,
87 ) -> bool {
88 let string: Vec<u16> = string.encode_utf16().collect();
89 if with_static {
90 let global = Object::global(cx);
91 unsafe {
92 ExecuteRegExp(
93 cx.as_ptr(),
94 global.handle().into(),
95 self.handle().into(),
96 string.as_ptr(),
97 string.len(),
98 index,
99 test,
100 rval.handle_mut().into(),
101 )
102 }
103 } else {
104 unsafe {
105 ExecuteRegExpNoStatics(
106 cx.as_ptr(),
107 self.handle().into(),
108 string.as_ptr(),
109 string.len(),
110 index,
111 test,
112 rval.handle_mut().into(),
113 )
114 }
115 }
116 }
117
118 pub fn check_syntax<'cx>(cx: &'cx Context, source: &str, flags: RegExpFlags) -> Result<(), Value<'cx>> {
119 let source: Vec<u16> = source.encode_utf16().collect();
120 let mut error = Value::undefined(cx);
121 let check = unsafe {
122 CheckRegExpSyntax(
123 cx.as_ptr(),
124 source.as_ptr(),
125 source.len(),
126 flags.into(),
127 error.handle_mut().into(),
128 )
129 };
130 if check {
131 if error.handle().is_undefined() {
132 Ok(())
133 } else {
134 Err(error)
135 }
136 } else {
137 Err(error)
138 }
139 }
140
141 pub fn is_regexp_raw(cx: &Context, object: *mut JSObject) -> bool {
143 rooted!(in(cx.as_ptr()) let object = object);
144 let mut is_regexp = false;
145 (unsafe { ObjectIsRegExp(cx.as_ptr(), object.handle().into(), &raw mut is_regexp) }) && is_regexp
146 }
147
148 pub fn is_regexp(cx: &Context, object: &Local<*mut JSObject>) -> bool {
150 let mut is_regexp = false;
151 (unsafe { ObjectIsRegExp(cx.as_ptr(), object.handle().into(), &raw mut is_regexp) }) && is_regexp
152 }
153}
154
155impl<'r> Deref for RegExp<'r> {
156 type Target = Local<'r, *mut JSObject>;
157
158 fn deref(&self) -> &Self::Target {
159 &self.re
160 }
161}
162
163impl DerefMut for RegExp<'_> {
164 fn deref_mut(&mut self) -> &mut Self::Target {
165 &mut self.re
166 }
167}