1use crate::{
2 convert::{IteratorJs, List},
3 value::Constructor,
4 Array, CString, Ctx, Error, IntoAtom, IntoJs, Object, Result, StdResult, StdString, String,
5 Value,
6};
7use std::{
8 cell::{Cell, RefCell},
9 collections::{BTreeMap, BTreeSet, HashMap, HashSet, LinkedList, VecDeque},
10 sync::{Mutex, RwLock},
11 time::SystemTime,
12};
13
14#[cfg(feature = "either")]
15use either::{Either, Left, Right};
16
17#[cfg(feature = "indexmap")]
18use indexmap::{IndexMap, IndexSet};
19
20impl<'js> IntoJs<'js> for Value<'js> {
21 fn into_js(self, _: &Ctx<'js>) -> Result<Value<'js>> {
22 Ok(self)
23 }
24}
25
26impl<'js> IntoJs<'js> for &Value<'js> {
27 fn into_js(self, _: &Ctx<'js>) -> Result<Value<'js>> {
28 Ok(self.clone())
29 }
30}
31
32impl<'js> IntoJs<'js> for StdString {
33 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
34 self.as_str().into_js(ctx)
35 }
36}
37
38impl<'js> IntoJs<'js> for &StdString {
39 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
40 self.as_str().into_js(ctx)
41 }
42}
43
44impl<'js> IntoJs<'js> for &str {
45 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
46 String::from_str(ctx.clone(), self).map(|String(value)| value)
47 }
48}
49
50impl<'js> IntoJs<'js> for char {
51 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
52 String::from_str(ctx.clone(), self.to_string().as_str()).map(|String(value)| value)
53 }
54}
55
56impl<'js> IntoJs<'js> for CString<'js> {
57 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
58 String::from_str(ctx.clone(), self.as_str()).map(|String(value)| value)
59 }
60}
61
62impl<'js> IntoJs<'js> for &CString<'js> {
63 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
64 String::from_str(ctx.clone(), self.as_str()).map(|String(value)| value)
65 }
66}
67
68impl<'js, T> IntoJs<'js> for &[T]
69where
70 for<'a> &'a T: IntoJs<'js>,
71{
72 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
73 self.iter()
74 .collect_js(ctx)
75 .map(|Array(value)| value.into_value())
76 }
77}
78
79impl<'js> IntoJs<'js> for () {
80 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
81 Ok(Value::new_undefined(ctx.clone()))
82 }
83}
84
85impl<'js> IntoJs<'js> for &() {
86 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
87 Ok(Value::new_undefined(ctx.clone()))
88 }
89}
90
91impl<'js, T> IntoJs<'js> for Option<T>
92where
93 T: IntoJs<'js>,
94{
95 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
96 Ok(match self {
97 Some(value) => value.into_js(ctx)?,
98 _ => Value::new_undefined(ctx.clone()),
99 })
100 }
101}
102
103impl<'js, T> IntoJs<'js> for &Option<T>
104where
105 for<'a> &'a T: IntoJs<'js>,
106{
107 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
108 Ok(match self {
109 Some(value) => value.into_js(ctx)?,
110 _ => Value::new_undefined(ctx.clone()),
111 })
112 }
113}
114
115impl<'js, T, E> IntoJs<'js> for StdResult<T, E>
116where
117 T: IntoJs<'js>,
118 Error: From<E>,
119{
120 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
121 self.map_err(Error::from)
122 .and_then(|value| value.into_js(ctx))
123 }
124}
125
126impl<'js, T, E> IntoJs<'js> for &StdResult<T, E>
127where
128 for<'a> &'a T: IntoJs<'js>,
129 for<'a> Error: From<&'a E>,
130{
131 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
132 self.as_ref()
133 .map_err(Error::from)
134 .and_then(|value| value.into_js(ctx))
135 }
136}
137
138#[cfg(feature = "either")]
140#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "either")))]
141impl<'js, L, R> IntoJs<'js> for Either<L, R>
142where
143 L: IntoJs<'js>,
144 R: IntoJs<'js>,
145{
146 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
147 match self {
148 Left(value) => value.into_js(ctx),
149 Right(value) => value.into_js(ctx),
150 }
151 }
152}
153
154#[cfg(feature = "either")]
156#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "either")))]
157impl<'js, L, R> IntoJs<'js> for &Either<L, R>
158where
159 for<'a> &'a L: IntoJs<'js>,
160 for<'a> &'a R: IntoJs<'js>,
161{
162 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
163 match self {
164 Left(value) => value.into_js(ctx),
165 Right(value) => value.into_js(ctx),
166 }
167 }
168}
169
170impl<'js, T> IntoJs<'js> for &Box<T>
171where
172 for<'r> &'r T: IntoJs<'js>,
173{
174 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
175 self.as_ref().into_js(ctx)
176 }
177}
178
179impl<'js, T> IntoJs<'js> for Box<T>
180where
181 T: IntoJs<'js>,
182{
183 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
184 (*self).into_js(ctx)
185 }
186}
187
188impl<'js, T> IntoJs<'js> for &Cell<T>
189where
190 T: IntoJs<'js> + Copy,
191{
192 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
193 self.get().into_js(ctx)
194 }
195}
196
197impl<'js, T> IntoJs<'js> for &RefCell<T>
198where
199 for<'r> &'r T: IntoJs<'js>,
200{
201 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
202 self.borrow().into_js(ctx)
203 }
204}
205
206impl<'js, T> IntoJs<'js> for Mutex<T>
207where
208 T: IntoJs<'js>,
209{
210 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
211 self.into_inner().expect("mutex was poisoned").into_js(ctx)
213 }
214}
215
216impl<'js, T> IntoJs<'js> for &Mutex<T>
217where
218 for<'r> &'r T: IntoJs<'js>,
219{
220 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
221 self.lock().expect("mutex was poisoned").into_js(ctx)
223 }
224}
225
226impl<'js, T> IntoJs<'js> for RwLock<T>
227where
228 T: IntoJs<'js>,
229{
230 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
231 self.into_inner().expect("lock was poisoned").into_js(ctx)
233 }
234}
235
236impl<'js, T> IntoJs<'js> for &RwLock<T>
237where
238 for<'r> &'r T: IntoJs<'js>,
239{
240 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
241 self.read().expect("lock was poisoned").into_js(ctx)
243 }
244}
245
246macro_rules! into_js_impls {
247 (cell: $($type:ident,)*) => {
249 $(
250 impl<'js, T> IntoJs<'js> for $type<T>
251 where
252 T: IntoJs<'js>,
253 {
254 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
255 self.into_inner().into_js(ctx)
256 }
257 }
258 )*
259 };
260
261 (tup: $($($type:ident)*,)*) => {
263 $(
264 impl<'js, $($type,)*> IntoJs<'js> for List<($($type,)*)>
265 where
266 $($type: IntoJs<'js>,)*
267 {
268 #[allow(non_snake_case)]
269 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
270 let ($($type,)*) = self.0;
271 let array = Array::new(ctx.clone())?;
272 $(array.set(into_js_impls!(@idx $type), $type)?;)*
273 Ok(array.into_value())
274 }
275 }
276 )*
277 };
278
279 (list: $($(#[$meta:meta])* $type:ident $({$param:ident})*,)*) => {
281 $(
282 $(#[$meta])*
283 impl<'js, T $(,$param)*> IntoJs<'js> for $type<T $(,$param)*>
284 where
285 T: IntoJs<'js>,
286 {
287 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
288 self.into_iter()
289 .collect_js(ctx)
290 .map(|Array(value)| value.into_value())
291 }
292 }
293
294 $(#[$meta])*
295 impl<'js, T $(,$param)*> IntoJs<'js> for &$type<T $(,$param)*>
296 where
297 for<'a> &'a T: IntoJs<'js>,
298 {
299 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
300 self.into_iter()
301 .collect_js(ctx)
302 .map(|Array(value)| value.into_value())
303 }
304 }
305 )*
306 };
307
308 (map: $($(#[$meta:meta])* $type:ident $({$param:ident})*,)*) => {
310 $(
311 $(#[$meta])*
312 impl<'js, K, V $(,$param)*> IntoJs<'js> for $type<K, V $(,$param)*>
313 where
314 K: IntoAtom<'js>,
315 V: IntoJs<'js>,
316 {
317 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
318 self.into_iter()
319 .collect_js(ctx)
320 .map(|Object(value)| value)
321 }
322 }
323
324 $(#[$meta])*
325 impl<'js, K, V $(,$param)*> IntoJs<'js> for &$type<K, V $(,$param)*>
326 where
327 for<'a> &'a K: IntoAtom<'js>,
328 for<'a> &'a V: IntoJs<'js>,
329 {
330 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
331 self.into_iter()
332 .collect_js(ctx)
333 .map(|Object(value)| value)
334 }
335 }
336 )*
337 };
338
339 (val: $($new:ident: $($type:ident)*,)*) => {
341 $(
342 $(
343 impl<'js> IntoJs<'js> for $type {
344 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
345 Ok(Value::$new(ctx.clone(), self as _))
346 }
347 }
348
349 impl<'js> IntoJs<'js> for &$type {
350 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
351 (*self).into_js(ctx)
352 }
353 }
354 )*
355 )*
356 };
357
358 (val: $($alt1:ident $alt2:ident => $($type:ty)*,)*) => {
361 $(
362 $(
363 impl<'js> IntoJs<'js> for $type {
364 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
365 let val = self as $alt1;
366 if val as $type == self {
367 val.into_js(ctx)
368 } else {
369 (self as $alt2).into_js(ctx)
370 }
371 }
372 }
373
374 impl<'js> IntoJs<'js> for &$type {
375 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
376 (*self).into_js(ctx)
377 }
378 }
379 )*
380 )*
381 };
382
383 (@idx A) => { 0 };
384 (@idx B) => { 1 };
385 (@idx C) => { 2 };
386 (@idx D) => { 3 };
387 (@idx E) => { 4 };
388 (@idx F) => { 5 };
389 (@idx G) => { 6 };
390 (@idx H) => { 7 };
391 (@idx I) => { 8 };
392 (@idx J) => { 9 };
393 (@idx K) => { 10 };
394 (@idx L) => { 11 };
395 (@idx M) => { 12 };
396 (@idx N) => { 13 };
397 (@idx O) => { 14 };
398 (@idx P) => { 15 };
399}
400
401into_js_impls! {
402 cell:
403 Cell,
404 RefCell,
405}
406
407into_js_impls! {
408 tup:
409 A,
410 A B,
411 A B C,
412 A B C D,
413 A B C D E,
414 A B C D E F,
415 A B C D E F G,
416 A B C D E F G H,
417 A B C D E F G H I,
418 A B C D E F G H I J,
419 A B C D E F G H I J K,
420 A B C D E F G H I J K L,
421 A B C D E F G H I J K L M,
422 A B C D E F G H I J K L M N,
423 A B C D E F G H I J K L M N O,
424 A B C D E F G H I J K L M N O P,
425}
426
427into_js_impls! {
428 list:
429 Vec,
431 VecDeque,
433 LinkedList,
435 HashSet {S},
437 BTreeSet,
439 #[cfg(feature = "indexmap")]
441 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "indexmap")))]
442 IndexSet {S},
443}
444
445into_js_impls! {
446 map:
447 HashMap {S},
449 BTreeMap,
451 #[cfg(feature = "indexmap")]
453 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "indexmap")))]
454 IndexMap {S},
455}
456
457into_js_impls! {
458 val:
459 new_bool: bool,
460 new_int: i8 i16 i32 u8 u16,
461 new_float: f32 f64,
462}
463
464into_js_impls! {
465 val:
466 i32 f64 => i64 u32 u64 usize isize,
467}
468
469fn millis_to_date<'js>(ctx: &Ctx<'js>, millis: i64) -> Result<Value<'js>> {
470 let date_ctor: Constructor = ctx.globals().get("Date")?;
471
472 date_ctor.construct((millis,))
473}
474
475impl<'js> IntoJs<'js> for SystemTime {
476 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
477 let millis = match self.duration_since(SystemTime::UNIX_EPOCH) {
478 Ok(duration) => {
480 let millis = duration.as_millis();
481
482 if millis > i64::MAX as _ {
483 return Err(Error::new_into_js_message(
484 "SystemTime",
485 "Date",
486 "Timestamp too big",
487 ));
488 }
489
490 millis as i64
491 }
492 Err(error) => {
494 let millis = error.duration().as_millis();
495
496 if millis > -(i64::MIN as i128) as _ {
497 return Err(Error::new_into_js_message(
498 "SystemTime",
499 "Date",
500 "Timestamp too small",
501 ));
502 }
503
504 (-(millis as i128)) as i64
505 }
506 };
507
508 millis_to_date(ctx, millis)
509 }
510}
511
512#[cfg(feature = "chrono")]
513impl<'js, Tz: chrono::TimeZone> IntoJs<'js> for chrono::DateTime<Tz> {
514 fn into_js(self, ctx: &Ctx<'js>) -> Result<Value<'js>> {
515 millis_to_date(ctx, self.timestamp_millis())
516 }
517}
518
519#[cfg(test)]
520mod test {
521
522 #[test]
523 fn char_to_js() {
524 use crate::{Context, IntoJs, Runtime};
525 let runtime = Runtime::new().unwrap();
526 let ctx = Context::full(&runtime).unwrap();
527
528 let c = 'a';
529
530 ctx.with(|ctx| {
531 let globs = ctx.globals();
532 globs.set("char", c.into_js(&ctx).unwrap()).unwrap();
533 let res: char = ctx.eval("globalThis.char").unwrap();
534 assert_eq!(c, res);
535
536 let rt = ctx.eval::<char, _>("''");
537 assert!(rt.is_err());
538 let rt = ctx.eval::<char, _>("'a'");
539 assert!(rt.is_ok());
540 let rt = ctx.eval::<char, _>("'ab'");
541 assert!(rt.is_err());
542 });
543 }
544
545 #[test]
546 fn system_time_to_js() {
547 use crate::{Context, IntoJs, Runtime};
548 use std::time::{Duration, SystemTime};
549
550 let runtime = Runtime::new().unwrap();
551 let ctx = Context::full(&runtime).unwrap();
552
553 let ts = SystemTime::now();
554 let millis = ts
555 .duration_since(SystemTime::UNIX_EPOCH)
556 .unwrap()
557 .as_millis();
558
559 ctx.with(|ctx| {
560 let globs = ctx.globals();
561 globs.set("ts", ts.into_js(&ctx).unwrap()).unwrap();
562 let res: i64 = ctx.eval("ts.getTime()").unwrap();
563 assert_eq!(millis, res as _);
564 });
565
566 let ts = SystemTime::UNIX_EPOCH - Duration::from_millis(123456);
567 let millis = SystemTime::UNIX_EPOCH
568 .duration_since(ts)
569 .unwrap()
570 .as_millis();
571
572 ctx.with(|ctx| {
573 let globs = ctx.globals();
574 globs.set("ts", ts.into_js(&ctx).unwrap()).unwrap();
575 let res: i64 = ctx.eval("ts.getTime()").unwrap();
576 assert_eq!(-(millis as i64), res as _);
577 });
578 }
579
580 #[cfg(feature = "chrono")]
581 #[test]
582 fn chrono_to_js() {
583 use crate::{Context, IntoJs, Runtime};
584 use chrono::Utc;
585
586 let runtime = Runtime::new().unwrap();
587 let ctx = Context::full(&runtime).unwrap();
588
589 let ts = Utc::now();
590 let millis = ts.timestamp_millis();
591
592 ctx.with(|ctx| {
593 let globs = ctx.globals();
594 globs.set("ts", ts.into_js(&ctx).unwrap()).unwrap();
595 let res: i64 = ctx.eval("ts.getTime()").unwrap();
596 assert_eq!(millis, res);
597 });
598 }
599}