den_stdlib_core/
lib.rs

1use derivative::Derivative;
2use derive_more::{From, Into};
3use quanta::{Clock, Instant};
4use rquickjs::{class::Trace, Coerced, Ctx, Exception, JsLifetime, Result};
5
6pub use crate::cancellation::CancellationTokenWrapper;
7
8#[derive(Trace, Derivative, From, Into, JsLifetime)]
9#[derivative(Clone, Debug)]
10#[rquickjs::class(rename = "Performance")]
11pub struct Performance {
12    #[qjs(skip_trace)]
13    clock:   Clock,
14    #[qjs(skip_trace)]
15    instant: Instant,
16}
17
18#[rquickjs::methods]
19impl Performance {
20    #[qjs(constructor)]
21    pub fn new() -> Result<Self> {
22        let clock = Clock::new();
23        let instant = clock.now();
24        Ok(Self { clock, instant })
25    }
26
27    pub fn now(self) -> u64 {
28        self.instant.elapsed().as_millis().try_into().unwrap()
29    }
30
31    #[qjs(get, enumerable, rename = "timeOrigin")]
32    pub fn time_origin(self) -> u64 {
33        self.clock.raw()
34    }
35}
36
37#[rquickjs::function()]
38pub fn btoa(value: Coerced<String>) -> Result<String> {
39    #[cfg(feature = "base64-simd")]
40    {
41        use base64_simd::STANDARD;
42        Ok(STANDARD.encode_to_string(value.as_bytes()))
43    }
44    #[cfg(feature = "base64")]
45    {
46        use base64::prelude::*;
47
48        Ok(BASE64_STANDARD.encode(value.as_bytes()))
49    }
50}
51
52#[rquickjs::function()]
53pub fn atob(ctx: Ctx<'_>, value: Coerced<String>) -> Result<String> {
54    #[cfg(feature = "base64-simd")]
55    {
56        use base64_simd::STANDARD;
57        match STANDARD.decode_to_vec(value.as_bytes()) {
58            Ok(decoded) => Ok(String::from_utf8(decoded)?),
59            Err(e) => Err(Exception::throw_internal(&ctx, &format!("{e}"))),
60        }
61    }
62    #[cfg(feature = "base64")]
63    {
64        use base64::prelude::*;
65        match BASE64_STANDARD.decode(value.as_bytes()) {
66            Ok(decoded) => Ok(String::from_utf8(decoded)?),
67            Err(e) => Err(Exception::throw_internal(&ctx, &format!("{e}"))),
68        }
69    }
70}
71
72#[rquickjs::function()]
73pub fn gc<'js>(ctx: Ctx<'js>) {
74    ctx.run_gc();
75}
76
77#[rquickjs::module(rename = "camelCase", rename_vars = "camelCase")]
78pub mod core {
79    use rquickjs::{
80        module::{Declarations, Exports},
81        Ctx, Result,
82    };
83
84    pub use crate::{cancellation::CancellationTokenWrapper, Performance};
85
86    #[qjs(declare)]
87    pub fn declare(declare: &Declarations) -> Result<()> {
88        declare
89            .declare("atob")?
90            .declare("btoa")?
91            .declare("performance")?
92            .declare("gc")?;
93        Ok(())
94    }
95
96    #[qjs(evaluate)]
97    pub fn evaluate<'js>(ctx: &Ctx<'js>, e: &Exports<'js>) -> Result<()> {
98        let performance = Performance::new()?;
99
100        e.export("atob", super::js_atob)?
101            .export("btoa", super::js_btoa)?
102            .export("performance", performance.clone())?
103            .export("gc", super::js_gc)?;
104
105        ctx.globals().set("atob", super::js_atob)?;
106        ctx.globals().set("btoa", super::js_btoa)?;
107        ctx.globals().set("performance", performance)?;
108        ctx.globals().set("gc", super::js_gc)?;
109        Ok(())
110    }
111}
112
113pub mod cancellation;