1pub use emlite;
2pub use emlite::Console;
3pub use emlite::FromVal;
4
5use alloc::{format, vec};
6
7pub use crate::any::{Any, AnyHandle};
8pub use crate::array::{
9 Array, ArrayBuffer, DataView, Endian, Float32Array, Float64Array, FrozenArray, Int8Array,
10 Int32Array, ObservableArray, TypedArray, Uint8Array, Uint32Array,
11};
12pub use crate::bigint::BigInt;
13pub use crate::date::Date;
14pub use crate::error::*;
15pub use crate::function::{Closure, Function};
16pub use crate::json::JSON;
17pub use crate::map::*;
18pub use crate::math::Math;
19pub use crate::null::Null;
20pub use crate::number::Number;
21pub use crate::object::Object;
22pub use crate::promise::Promise;
23pub use crate::record::Record;
24pub use crate::reflect::Reflect;
25pub use crate::regexp::{RegExp, RegExpFlags};
26pub use crate::response::{fetch, fetch_val};
27pub use crate::set::*;
28pub use crate::string::JsString;
29pub use crate::symbol::Symbol;
30pub use crate::text::{TextDecoder, TextEncoder};
31pub use crate::time::*;
32pub use crate::undefined::Undefined;
33pub use crate::url::URL;
34
35pub fn parse_int(src: &str, radix: Option<i32>) -> Result<i32, JsError> {
56 let g = emlite::Val::global("parseInt");
57 let result = match radix {
58 Some(r) => {
59 if !(2..=36).contains(&r) {
60 return Err(JsError::new("Radix must be between 2 and 36"));
61 }
62 g.invoke(&[src.into(), r.into()])
63 }
64 None => g.invoke(&[src.into()]),
65 };
66
67 if is_nan(&result) {
68 Err(JsError::new(&format!("Invalid number format: '{}'", src)))
69 } else {
70 Ok(result.as_::<i32>())
71 }
72}
73
74pub fn parse_float(src: &str) -> Result<f64, JsError> {
94 let result = emlite::Val::global("parseFloat").invoke(&[src.into()]);
95
96 if is_nan(&result) {
97 Err(JsError::new(&format!("Invalid number format: '{}'", src)))
98 } else {
99 Ok(result.as_::<f64>())
100 }
101}
102
103pub trait DynCast
109where
110 Self: AsRef<emlite::Val> + Into<emlite::Val> + AsMut<emlite::Val>,
111{
112 fn has_type<T>(&self) -> bool
113 where
114 T: DynCast,
115 {
116 T::is_type_of(self.as_ref())
117 }
118
119 fn dyn_into<T>(self) -> Result<T, Self>
120 where
121 T: DynCast,
122 {
123 if self.has_type::<T>() {
124 Ok(self.unchecked_into())
125 } else {
126 Err(self)
127 }
128 }
129
130 fn dyn_ref<T>(&self) -> Option<&T>
131 where
132 T: DynCast,
133 {
134 if self.has_type::<T>() {
135 Some(self.unchecked_ref())
136 } else {
137 None
138 }
139 }
140
141 fn dyn_mut<T>(&mut self) -> Option<&mut T>
142 where
143 T: DynCast,
144 {
145 if self.has_type::<T>() {
146 Some(self.unchecked_mut())
147 } else {
148 None
149 }
150 }
151
152 fn unchecked_into<T>(self) -> T
153 where
154 T: DynCast,
155 {
156 T::unchecked_from_val(self.into())
157 }
158
159 fn unchecked_ref<T>(&self) -> &T
160 where
161 T: DynCast,
162 {
163 T::unchecked_from_val_ref(self.as_ref())
164 }
165
166 fn unchecked_mut<T>(&mut self) -> &mut T
167 where
168 T: DynCast,
169 {
170 T::unchecked_from_val_mut(self.as_mut())
171 }
172
173 fn is_instance_of<T>(&self) -> bool
174 where
175 T: DynCast,
176 {
177 T::instanceof(self.as_ref())
178 }
179
180 fn instanceof(val: &emlite::Val) -> bool;
182
183 fn is_type_of(val: &emlite::Val) -> bool {
185 Self::instanceof(val)
186 }
187
188 fn unchecked_from_val(v: emlite::Val) -> Self;
190
191 fn unchecked_from_val_ref(v: &emlite::Val) -> &Self;
193
194 fn unchecked_from_val_mut(v: &mut emlite::Val) -> &mut Self;
196}
197
198#[cold]
200#[inline(never)]
201pub fn throw_str(s: &str) -> ! {
202 throw_val(s.into())
203}
204
205#[cold]
207#[inline(never)]
208pub fn throw_val(s: Any) -> ! {
209 let handle = s.as_handle();
210 core::mem::forget(s);
211 emlite::Val::throw(emlite::Val::take_ownership(handle));
212}
213
214pub trait UnwrapThrowExt<T>: Sized {
216 fn unwrap_throw(self) -> T {
217 let loc = core::panic::Location::caller();
218 let msg = alloc::format!(
219 "called `{}::unwrap_throw()` ({}:{}:{})",
220 core::any::type_name::<Self>(),
221 loc.file(),
222 loc.line(),
223 loc.column()
224 );
225 self.expect_throw(&msg)
226 }
227
228 fn expect_throw(self, message: &str) -> T;
229}
230
231impl<T> UnwrapThrowExt<T> for Option<T> {
233 fn unwrap_throw(self) -> T {
234 const MSG: &str = "called `Option::unwrap_throw()` on a `None` value";
235 if let Some(val) = self {
236 val
237 } else if cfg!(debug_assertions) {
238 let loc = core::panic::Location::caller();
239 let msg = alloc::format!("{} ({}:{}:{})", MSG, loc.file(), loc.line(), loc.column(),);
240
241 throw_str(&msg)
242 } else {
243 throw_str(MSG)
244 }
245 }
246
247 fn expect_throw(self, message: &str) -> T {
248 if let Some(val) = self {
249 val
250 } else if cfg!(debug_assertions) {
251 let loc = core::panic::Location::caller();
252 let msg = alloc::format!(
253 "{} ({}:{}:{})",
254 message,
255 loc.file(),
256 loc.line(),
257 loc.column(),
258 );
259
260 throw_str(&msg)
261 } else {
262 throw_str(message)
263 }
264 }
265}
266
267impl<T, E> UnwrapThrowExt<T> for Result<T, E>
269where
270 E: core::fmt::Debug,
271{
272 fn unwrap_throw(self) -> T {
273 const MSG: &str = "called `Result::unwrap_throw()` on an `Err` value";
274 match self {
275 Ok(val) => val,
276 Err(err) => {
277 if cfg!(debug_assertions) {
278 let loc = core::panic::Location::caller();
279 let msg = alloc::format!(
280 "{} ({}:{}:{}): {:?}",
281 MSG,
282 loc.file(),
283 loc.line(),
284 loc.column(),
285 err
286 );
287
288 throw_str(&msg)
289 } else {
290 throw_str(MSG)
291 }
292 }
293 }
294 }
295
296 fn expect_throw(self, message: &str) -> T {
297 match self {
298 Ok(val) => val,
299 Err(err) => {
300 if cfg!(debug_assertions) {
301 let loc = core::panic::Location::caller();
302 let msg = alloc::format!(
303 "{} ({}:{}:{}): {:?}",
304 message,
305 loc.file(),
306 loc.line(),
307 loc.column(),
308 err
309 );
310
311 throw_str(&msg)
312 } else {
313 throw_str(message)
314 }
315 }
316 }
317 }
318}
319
320pub fn btoa(data: &JsString) -> Result<JsString, JsError> {
337 let result = emlite::Val::global("btoa").invoke(&[data.into()]);
338 result.as_::<Result<JsString, JsError>>()
339}
340
341pub fn atob(encoded: &JsString) -> Result<JsString, JsError> {
358 let result = emlite::Val::global("atob").invoke(&[encoded.into()]);
359 result.as_::<Result<JsString, JsError>>()
360}
361
362pub fn is_nan<V: Into<emlite::Val>>(value: V) -> bool {
378 emlite::Val::global("isNaN")
379 .invoke(&[value.into()])
380 .as_::<bool>()
381}
382
383pub fn queue_microtask<C: Into<emlite::Val>>(callback: C) {
398 emlite::Val::global("queueMicrotask").invoke(&[callback.into()]);
399}
400
401pub fn import_module(specifier: &str) -> Promise<Result<Object, JsError>> {
417 let import_promise = emlite::Val::global("import").invoke(&[specifier.into()]);
418 Promise::take_ownership(import_promise.as_handle())
419}
420
421pub fn require_module(specifier: &str) -> Result<Object, JsError> {
437 let require_fn = emlite::Val::global("require");
438 if require_fn.is_undefined() {
439 return Err(JsError::new("require is not available in this environment"));
440 }
441
442 let module_exports = require_fn.invoke(&[specifier.into()]);
443 Ok(Object::from_val(&module_exports.as_::<Any>()))
444}
445
446pub fn create_require<V: Into<emlite::Val>>(import_meta_url: V) -> Result<Function, JsError> {
464 let module_obj = emlite::Val::global("module");
465 if module_obj.is_undefined() {
466 return Err(JsError::new(
467 "module.createRequire not supported in this environment",
468 ));
469 }
470
471 let create_require_fn = module_obj.get("createRequire");
472 if create_require_fn.is_undefined() {
473 return Err(JsError::new("module.createRequire not available"));
474 }
475
476 let require_fn = create_require_fn.invoke(&[import_meta_url.into()]);
477 Ok(Function::from_val(&require_fn.as_::<Any>()))
478}
479
480#[derive(Clone, Debug)]
482pub struct JsStructuredSerializeOptions {
483 inner: emlite::Val,
484}
485
486impl JsStructuredSerializeOptions {
487 pub fn new() -> Self {
489 Self {
490 inner: emlite::Val::object(),
491 }
492 }
493
494 pub fn transfer(&self) -> Option<TypedArray<Object>> {
496 let val = self.inner.get("transfer");
497 if val.is_undefined() {
498 None
499 } else {
500 Some(val.as_::<TypedArray<Object>>())
501 }
502 }
503
504 pub fn set_transfer(&self, transfer: &TypedArray<Object>) {
506 self.inner.set("transfer", transfer);
507 }
508}
509
510impl Default for JsStructuredSerializeOptions {
511 fn default() -> Self {
512 Self::new()
513 }
514}
515
516impl AsRef<emlite::Val> for JsStructuredSerializeOptions {
517 fn as_ref(&self) -> &emlite::Val {
518 &self.inner
519 }
520}
521
522impl From<JsStructuredSerializeOptions> for emlite::Val {
523 fn from(options: JsStructuredSerializeOptions) -> Self {
524 options.inner
525 }
526}
527
528impl From<&JsStructuredSerializeOptions> for emlite::Val {
529 fn from(options: &JsStructuredSerializeOptions) -> Self {
530 options.inner.clone()
531 }
532}
533
534pub fn structured_clone<T>(value: &T, options: Option<&JsStructuredSerializeOptions>) -> T
554where
555 T: emlite::FromVal + AsRef<emlite::Val>,
556{
557 let args = match options {
558 Some(opts) => vec![value.as_ref().clone(), opts.into()],
559 None => vec![value.as_ref().clone()],
560 };
561
562 emlite::Val::global("structuredClone")
563 .invoke(&args)
564 .as_::<T>()
565}