Thanks to visit codestin.com
Credit goes to docs.rs

rustyscript/
snapshot_builder.rs

1use std::{path::Path, rc::Rc, time::Duration};
2
3use deno_core::{JsRuntimeForSnapshot, PollEventLoopOptions};
4use tokio_util::sync::CancellationToken;
5
6use crate::{
7    async_bridge::{AsyncBridge, AsyncBridgeExt, TokioRuntime},
8    inner_runtime::{InnerRuntime, RuntimeOptions},
9    Error, Module, ModuleHandle,
10};
11
12/// A more restricted version of the `Runtime` struct that is used to create a snapshot of the runtime state
13/// This runtime should ONLY be used to create a snapshot, and not for normal use
14///
15/// Snapshots can be used to massively decrease the startup time of a Runtime instance (15ms -> 3ms) by pre-loading
16/// extensions and modules into the runtime state before it is created. A snapshot can be used on any runtime with
17/// the same set of extensions and options as the runtime that created it.
18///
19/// This struct is only available when the `snapshot_builder` feature is enabled
20/// Once you've set up the runtime, you can call `into_snapshot` to get the snapshot
21///
22/// You should save it to a file and load it with `include_bytes!` in order to use it
23/// in the `RuntimeOptions` struct's `startup_snapshot` field
24///
25/// # Example
26///
27/// ```rust
28/// use rustyscript::{SnapshotBuilder, Module, Error};
29/// use std::fs;
30///
31/// # fn main() -> Result<(), Error> {
32/// let module = Module::new("example.js", "export function example() { return 42; }");
33/// let snapshot = SnapshotBuilder::new(Default::default())?
34///    .with_module(&module)?
35///    .finish();
36///
37/// // Save the snapshot to a file
38/// fs::write("snapshot.bin", snapshot)?;
39///
40/// // To use the snapshot, load it with `include_bytes!` into the `RuntimeOptions` struct:
41/// // const STARTUP_SNAPSHOT: &[u8] = include_bytes!("snapshot.bin");
42/// // RuntimeOptions {
43/// //     startup_snapshot: Some(STARTUP_SNAPSHOT),
44/// //     ..Default::default()
45/// // };
46///
47/// # Ok(())
48/// # }
49/// ```
50pub struct SnapshotBuilder {
51    inner: InnerRuntime<deno_core::JsRuntimeForSnapshot>,
52    tokio: AsyncBridge,
53}
54impl SnapshotBuilder {
55    /// Creates a new instance of the runtime with the provided options.
56    ///
57    /// # Arguments
58    /// * `options` - A `RuntimeOptions` struct that specifies the configuration options for the runtime.
59    ///
60    /// # Returns
61    /// A `Result` containing either the initialized runtime instance on success (`Ok`) or an error on failure (`Err`).
62    ///
63    /// # Example
64    /// ```rust
65    /// use rustyscript::{ json_args, Runtime, RuntimeOptions, Module };
66    /// use std::time::Duration;
67    ///
68    /// # fn main() -> Result<(), rustyscript::Error> {
69    /// // Creates a runtime that will attempt to run function load() on start
70    /// // And which will time-out after 50ms
71    /// let mut runtime = Runtime::new(RuntimeOptions {
72    ///     default_entrypoint: Some("load".to_string()),
73    ///     timeout: Duration::from_millis(50),
74    ///     ..Default::default()
75    /// })?;
76    ///
77    /// let module = Module::new("test.js", "
78    ///     export const load = () => {
79    ///         return 'Hello World!';
80    ///     }
81    /// ");
82    ///
83    /// let module_handle = runtime.load_module(&module)?;
84    /// let value: String = runtime.call_entrypoint(&module_handle, json_args!())?;
85    /// assert_eq!("Hello World!", value);
86    /// # Ok(())
87    /// # }
88    /// ```
89    ///
90    /// # Errors
91    /// Can fail if the tokio runtime cannot be created,
92    /// Or if the deno runtime initialization fails (usually issues with extensions)
93    ///
94    pub fn new(options: RuntimeOptions) -> Result<Self, Error> {
95        let tokio = AsyncBridge::new(options.timeout)?;
96        let inner = InnerRuntime::new(options, tokio.heap_exhausted_token())?;
97        Ok(Self { inner, tokio })
98    }
99
100    /// Creates a new instance of the runtime with the provided options and a pre-configured tokio runtime.
101    /// See [`crate::Runtime::new`] for more information.
102    ///
103    /// # Errors
104    /// Can fail if the deno runtime initialization fails (usually issues with extensions)
105    pub fn with_tokio_runtime(
106        options: RuntimeOptions,
107        tokio: Rc<tokio::runtime::Runtime>,
108    ) -> Result<Self, Error> {
109        let tokio = AsyncBridge::with_tokio_runtime(options.timeout, tokio);
110        let inner = InnerRuntime::new(options, tokio.heap_exhausted_token())?;
111        Ok(Self { inner, tokio })
112    }
113
114    /// Creates a new instance of the runtime with the provided options and a borrowed tokio runtime handle.  
115    /// See [`Runtime::new`] for more information.
116    ///
117    /// # Errors
118    /// Can fail if the deno runtime initialization fails (usually issues with extensions)
119    pub fn with_tokio_runtime_handle(
120        options: RuntimeOptions,
121        handle: tokio::runtime::Handle,
122    ) -> Result<Self, Error> {
123        let tokio = AsyncBridge::with_runtime_handle(options.timeout, handle);
124        let inner = InnerRuntime::new(options, tokio.heap_exhausted_token())?;
125        Ok(Self { inner, tokio })
126    }
127
128    /// Access the underlying deno runtime instance directly
129    pub fn deno_runtime(&mut self) -> &mut deno_core::JsRuntime {
130        self.inner.deno_runtime()
131    }
132
133    /// Access the underlying tokio runtime used for blocking operations
134    #[must_use]
135    pub fn tokio_runtime(&self) -> TokioRuntime {
136        self.tokio.tokio_runtime()
137    }
138
139    /// Returns the timeout for the runtime
140    #[must_use]
141    pub fn timeout(&self) -> std::time::Duration {
142        self.tokio.timeout()
143    }
144
145    /// Returns the heap exhausted token for the runtime
146    /// Used to detect when the runtime has run out of memory
147    #[must_use]
148    pub fn heap_exhausted_token(&self) -> CancellationToken {
149        self.tokio.heap_exhausted_token()
150    }
151
152    /// Destroy the v8 runtime, releasing all resources
153    /// Then the internal tokio runtime will be returned
154    #[must_use]
155    pub fn into_tokio_runtime(self) -> TokioRuntime {
156        self.tokio.into_tokio_runtime()
157    }
158
159    /// Set the current working directory for the runtime
160    /// This is used to resolve relative paths in the module loader
161    ///
162    /// The runtime will begin with the current working directory of the process
163    ///
164    /// # Errors
165    /// Can fail if the given path is not valid
166    pub fn set_current_dir(&mut self, path: impl AsRef<Path>) -> Result<&Path, Error> {
167        self.inner.set_current_dir(path)
168    }
169
170    /// Get the current working directory for the runtime
171    /// This is used to resolve relative paths in the module loader
172    ///
173    /// The runtime will begin with the current working directory of the process
174    #[must_use]
175    pub fn current_dir(&self) -> &Path {
176        self.inner.current_dir()
177    }
178
179    /// Advance the JS event loop by a single tick
180    /// See [`crate::Runtime::await_event_loop`] for fully running the event loop
181    ///
182    /// Returns true if the event loop has pending work, or false if it has completed
183    ///
184    /// # Arguments
185    /// * `options` - Options for the event loop polling, see [`deno_core::PollEventLoopOptions`]
186    ///
187    /// # Errors
188    /// Can fail if a runtime error occurs during the event loop's execution
189    pub fn advance_event_loop(&mut self, options: PollEventLoopOptions) -> Result<bool, Error> {
190        self.block_on(|runtime| async move { runtime.inner.advance_event_loop(options).await })
191    }
192
193    /// Run the JS event loop to completion, or until a timeout is reached
194    /// Required when using the `_immediate` variants of functions
195    ///
196    /// # Arguments
197    /// * `options` - Options for the event loop polling, see [`deno_core::PollEventLoopOptions`]
198    /// * `timeout` - Optional timeout for the event loop
199    ///
200    /// # Errors
201    /// Can fail if a runtime error occurs during the event loop's execution
202    pub async fn await_event_loop(
203        &mut self,
204        options: PollEventLoopOptions,
205        timeout: Option<Duration>,
206    ) -> Result<(), Error> {
207        self.inner.await_event_loop(options, timeout).await
208    }
209
210    /// Run the JS event loop to completion, or until a timeout is reached
211    /// Required when using the `_immediate` variants of functions
212    ///
213    /// This is the blocking variant of [`crate::Runtime::await_event_loop`]
214    ///
215    /// # Arguments
216    /// * `options` - Options for the event loop polling, see [`deno_core::PollEventLoopOptions`]
217    /// * `timeout` - Optional timeout for the event loop
218    ///
219    /// # Errors
220    /// Can fail if a runtime error occurs during the event loop's execution
221    pub fn block_on_event_loop(
222        &mut self,
223        options: deno_core::PollEventLoopOptions,
224        timeout: Option<Duration>,
225    ) -> Result<(), Error> {
226        self.block_on(|runtime| async move { runtime.await_event_loop(options, timeout).await })
227    }
228
229    /// Remove and return a value from the state, if one exists
230    /// ```rust
231    /// use rustyscript::{ Runtime };
232    ///
233    /// # fn main() -> Result<(), rustyscript::Error> {
234    /// let mut runtime = Runtime::new(Default::default())?;
235    /// runtime.put("test".to_string())?;
236    /// let value: String = runtime.take().unwrap();
237    /// assert_eq!(value, "test");
238    /// # Ok(())
239    /// # }
240    /// ```
241    pub fn take<T>(&mut self) -> Option<T>
242    where
243        T: 'static,
244    {
245        self.inner.take()
246    }
247
248    /// Add a value to the state
249    /// Only one value of each type is stored - additional calls to put overwrite the
250    /// old value
251    ///
252    /// # Errors
253    /// Can fail if the inner state cannot be borrowed mutably
254    ///
255    /// ```rust
256    /// use rustyscript::{ Runtime };
257    ///
258    /// # fn main() -> Result<(), rustyscript::Error> {
259    /// let mut runtime = Runtime::new(Default::default())?;
260    /// runtime.put("test".to_string())?;
261    /// let value: String = runtime.take().unwrap();
262    /// assert_eq!(value, "test");
263    /// # Ok(())
264    /// # }
265    /// ```
266    pub fn put<T>(&mut self, value: T) -> Result<(), Error>
267    where
268        T: 'static,
269    {
270        self.inner.put(value)
271    }
272
273    /// Evaluate a piece of non-ECMAScript-module JavaScript code  
274    /// The expression is evaluated in the global context, so changes persist
275    ///
276    /// Blocks on promise resolution, and runs the event loop to completion
277    ///
278    /// Asynchronous code is supported, partially
279    /// - Top-level await is not supported
280    /// - The event loop will be run to completion after the expression is evaluated
281    ///
282    /// For top-level await support, use one of:
283    /// - `call_function_async`
284    /// - `call_stored_function_async`
285    /// - `load_module_async`
286    /// - `load_modules_async`
287    ///
288    /// Or any of the `_immmediate` variants, paired with [`crate::js_value::Promise`]
289    ///
290    /// # Arguments
291    /// * `expr` - A string representing the JavaScript expression to evaluate
292    ///
293    /// # Returns
294    /// A `Result` containing the deserialized result of the expression (`T`)  
295    /// or an error (`Error`) if the expression cannot be evaluated or if the
296    /// result cannot be deserialized.
297    ///
298    /// # Errors
299    /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized into the requested type
300    ///
301    /// See [`crate::Runtime::eval`] for an example
302    pub fn eval<T>(&mut self, expr: impl ToString) -> Result<T, Error>
303    where
304        T: serde::de::DeserializeOwned,
305    {
306        self.block_on(|runtime| async move { runtime.eval_async(expr).await })
307    }
308
309    /// Evaluate a piece of non-ECMAScript-module JavaScript code  
310    /// The expression is evaluated in the global context, so changes persist
311    ///
312    /// Awaits promise resolution, and runs the event loop to completion
313    ///
314    /// Asynchronous code is supported, partially
315    /// - Top-level await is not supported
316    /// - The event loop will be run to completion after the expression is evaluated
317    ///
318    /// For top-level await support, use one of:
319    /// - `call_function_async`
320    /// - `call_stored_function_async`
321    /// - `load_module_async`
322    /// - `load_modules_async`
323    ///
324    /// Or any of the `_immmediate` variants, paired with [`crate::js_value::Promise`]
325    ///
326    /// # Arguments
327    /// * `expr` - A string representing the JavaScript expression to evaluate
328    ///
329    /// # Returns
330    /// A `Result` containing the deserialized result of the expression (`T`)  
331    /// or an error (`Error`) if the expression cannot be evaluated or if the
332    /// result cannot be deserialized.
333    ///
334    /// # Errors
335    /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized into the requested type
336    ///
337    /// # Example
338    /// For an example, see [`crate::Runtime::eval`]
339    pub async fn eval_async<T>(&mut self, expr: impl ToString) -> Result<T, Error>
340    where
341        T: serde::de::DeserializeOwned,
342    {
343        let result = self.inner.eval(expr.to_string()).await?;
344        let result = self.inner.resolve_with_event_loop(result).await?;
345        self.inner.decode_value(result)
346    }
347
348    /// Evaluate a piece of non-ECMAScript-module JavaScript code  
349    /// The expression is evaluated in the global context, so changes persist
350    ///
351    /// Does not await promise resolution, or run the event loop  
352    /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]  
353    /// The event loop should be run using [`crate::Runtime::await_event_loop`]
354    ///
355    /// Note that this function needs to be async because calls to `setTimeout` must be evaluated from within an async runtime.
356    ///
357    /// Asynchronous code is supported, partially
358    /// - Top-level await is not supported
359    ///
360    /// For top-level await support, use one of:
361    /// - `call_function_async`
362    /// - `call_stored_function_async`
363    /// - `load_module_async`
364    /// - `load_modules_async`
365    ///
366    /// Or any of the `_immmediate` variants, paired with [`crate::js_value::Promise`]
367    ///
368    /// # Arguments
369    /// * `expr` - A string representing the JavaScript expression to evaluate
370    ///
371    /// # Returns
372    /// A `Result` containing the deserialized result of the expression (`T`)  
373    /// or an error (`Error`) if the expression cannot be evaluated or if the
374    /// result cannot be deserialized.
375    ///
376    /// # Errors
377    /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized into the requested type
378    ///
379    /// # Example
380    /// For an example, see [`crate::Runtime::eval`]
381    pub async fn eval_immediate<T>(&mut self, expr: impl ToString) -> Result<T, Error>
382    where
383        T: serde::de::DeserializeOwned,
384    {
385        let result = self.inner.eval(expr.to_string()).await?;
386        self.inner.decode_value(result)
387    }
388
389    /// Calls a javascript function within the Deno runtime by its name and deserializes its return value.
390    /// Returns a future that resolves when:
391    /// - The event loop is resolved, and
392    /// - If the value is a promise, the promise is resolved
393    ///
394    /// Note that synchronous functions are run synchronously. Returned promises will be run asynchronously, however.
395    ///
396    /// See [`crate::Runtime::call_function`] for an example
397    ///
398    /// # Arguments
399    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
400    /// * `name` - A string representing the name of the javascript function to call.
401    /// * `args` - The arguments to pass to the function
402    ///
403    /// # Returns
404    /// A `Result` containing the deserialized result of the function call (`T`)
405    /// or an error (`Error`) if the function cannot be found, if there are issues with
406    /// calling the function, or if the result cannot be deserialized.
407    ///
408    /// # Errors
409    /// Fails if the function cannot be found, if there are issues with calling the function,
410    /// Or if the result cannot be deserialized into the requested type
411    pub async fn call_function_async<T>(
412        &mut self,
413        module_context: Option<&ModuleHandle>,
414        name: &str,
415        args: &impl serde::ser::Serialize,
416    ) -> Result<T, Error>
417    where
418        T: deno_core::serde::de::DeserializeOwned,
419    {
420        let function = self.inner.get_function_by_name(module_context, name)?;
421        let result = self
422            .inner
423            .call_function_by_ref(module_context, &function, args)?;
424        let result = self.inner.resolve_with_event_loop(result).await?;
425        self.inner.decode_value(result)
426    }
427
428    /// Calls a javascript function within the Deno runtime by its name and deserializes its return value.
429    /// Blocks until:
430    /// - The event loop is resolved, and
431    /// - If the value is a promise, the promise is resolved
432    ///
433    /// # Arguments
434    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
435    /// * `name` - A string representing the name of the javascript function to call.
436    /// * `args` - The arguments to pass to the function
437    ///
438    /// # Returns
439    /// A `Result` containing the deserialized result of the function call (`T`)
440    /// or an error (`Error`) if the function cannot be found, if there are issues with
441    /// calling the function, or if the result cannot be deserialized.
442    ///
443    /// # Errors
444    /// Fails if the function cannot be found, if there are issues with calling the function,
445    /// Or if the result cannot be deserialized into the requested type
446    ///
447    /// # Example
448    ///
449    /// ```rust
450    /// use rustyscript::{ json_args, Runtime, Module, Error };
451    ///
452    /// # fn main() -> Result<(), Error> {
453    /// let mut runtime = Runtime::new(Default::default())?;
454    /// let module = Module::new("/path/to/module.js", "export function f() { return 2; };");
455    /// let module = runtime.load_module(&module)?;
456    /// let value: usize = runtime.call_function(Some(&module), "f", json_args!())?;
457    /// # Ok(())
458    /// # }
459    /// ```
460    pub fn call_function<T>(
461        &mut self,
462        module_context: Option<&ModuleHandle>,
463        name: &str,
464        args: &impl serde::ser::Serialize,
465    ) -> Result<T, Error>
466    where
467        T: deno_core::serde::de::DeserializeOwned,
468    {
469        self.block_on(|runtime| async move {
470            runtime
471                .call_function_async(module_context, name, args)
472                .await
473        })
474    }
475
476    /// Calls a javascript function within the Deno runtime by its name and deserializes its return value.
477    /// Will not attempt to resolve promises, or run the event loop
478    /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]
479    /// The event loop should be run using [`crate::Runtime::await_event_loop`]
480    ///
481    /// # Arguments
482    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
483    /// * `name` - A string representing the name of the javascript function to call.
484    /// * `args` - The arguments to pass to the function
485    ///
486    /// # Returns
487    /// A `Result` containing the deserialized result of the function call (`T`)
488    /// or an error (`Error`) if the function cannot be found, if there are issues with
489    /// calling the function, or if the result cannot be deserialized.
490    ///
491    /// # Errors
492    /// Fails if the function cannot be found, if there are issues with calling the function,
493    /// Or if the result cannot be deserialized into the requested type
494    ///
495    /// # Example
496    ///
497    /// ```rust
498    /// use rustyscript::{ json_args, Runtime, Module, Error };
499    ///
500    /// # fn main() -> Result<(), Error> {
501    /// let mut runtime = Runtime::new(Default::default())?;
502    /// let module = Module::new("/path/to/module.js", "export function f() { return 2; };");
503    /// let module = runtime.load_module(&module)?;
504    /// let value: usize = runtime.call_function_immediate(Some(&module), "f", json_args!())?;
505    /// # Ok(())
506    /// # }
507    /// ```
508    pub fn call_function_immediate<T>(
509        &mut self,
510        module_context: Option<&ModuleHandle>,
511        name: &str,
512        args: &impl serde::ser::Serialize,
513    ) -> Result<T, Error>
514    where
515        T: deno_core::serde::de::DeserializeOwned,
516    {
517        let function = self.inner.get_function_by_name(module_context, name)?;
518        let result = self
519            .inner
520            .call_function_by_ref(module_context, &function, args)?;
521        self.inner.decode_value(result)
522    }
523
524    /// Get a value from a runtime instance
525    /// Blocks until:
526    /// - The event loop is resolved, and
527    /// - If the value is a promise, the promise is resolved
528    ///
529    /// # Arguments
530    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
531    /// * `name` - A string representing the name of the value to find
532    ///
533    /// # Returns
534    /// A `Result` containing the deserialized result or an error (`Error`) if the value cannot be found,
535    /// Or if the result cannot be deserialized into the requested type
536    ///
537    /// # Errors
538    /// Can fail if the value cannot be found, or if the result cannot be deserialized.
539    ///
540    /// # Example
541    ///
542    /// ```rust
543    /// use rustyscript::{ Runtime, Module, Error };
544    ///
545    /// # fn main() -> Result<(), Error> {
546    /// let mut runtime = Runtime::new(Default::default())?;
547    /// let module = Module::new("/path/to/module.js", "globalThis.my_value = 2;");
548    /// let module = runtime.load_module(&module)?;
549    /// let value: usize = runtime.get_value(Some(&module), "my_value")?;
550    /// # Ok(())
551    /// # }
552    /// ```
553    pub fn get_value<T>(
554        &mut self,
555        module_context: Option<&ModuleHandle>,
556        name: &str,
557    ) -> Result<T, Error>
558    where
559        T: serde::de::DeserializeOwned,
560    {
561        self.block_on(|runtime| async move { runtime.get_value_async(module_context, name).await })
562    }
563
564    /// Get a value from a runtime instance
565    /// Returns a future that resolves when:
566    /// - The event loop is resolved, and
567    /// - If the value is a promise, the promise is resolved
568    ///
569    /// See [`crate::Runtime::get_value`] for an example
570    ///
571    /// # Arguments
572    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
573    /// * `name` - A string representing the name of the value to find
574    ///
575    /// # Returns
576    /// A `Result` containing the deserialized result or an error (`Error`) if the value cannot be found,
577    /// Or if the result cannot be deserialized into the requested type
578    ///
579    /// # Errors
580    /// Can fail if the value cannot be found, or if the result cannot be deserialized.
581    pub async fn get_value_async<T>(
582        &mut self,
583        module_context: Option<&ModuleHandle>,
584        name: &str,
585    ) -> Result<T, Error>
586    where
587        T: serde::de::DeserializeOwned,
588    {
589        let result = self.inner.get_value_ref(module_context, name)?;
590        let result = self.inner.resolve_with_event_loop(result).await?;
591        self.inner.decode_value(result)
592    }
593
594    /// Get a value from a runtime instance
595    /// Will not attempt to resolve promises, or run the event loop
596    /// Promises can be returned by specifying the return type as [`crate::js_value::Promise`]
597    /// The event loop should be run using [`crate::Runtime::await_event_loop`]
598    ///
599    /// # Arguments
600    /// * `module_context` - Optional handle to a module to search - if None, or if the search fails, the global context is used
601    /// * `name` - A string representing the name of the value to find
602    ///
603    /// # Returns
604    /// A `Result` containing the deserialized result or an error (`Error`) if the value cannot be found,
605    /// Or if the result cannot be deserialized into the requested type
606    ///
607    /// # Errors
608    /// Can fail if the value cannot be found, or if the result cannot be deserialized.
609    ///
610    /// # Example
611    ///
612    /// ```rust
613    /// use rustyscript::{ Runtime, Module, Error };
614    ///
615    /// # fn main() -> Result<(), Error> {
616    /// let mut runtime = Runtime::new(Default::default())?;
617    /// let module = Module::new("/path/to/module.js", "globalThis.my_value = 2;");
618    /// let module = runtime.load_module(&module)?;
619    /// let value: usize = runtime.get_value_immediate(Some(&module), "my_value")?;
620    /// # Ok(())
621    /// # }
622    /// ```
623    pub fn get_value_immediate<T>(
624        &mut self,
625        module_context: Option<&ModuleHandle>,
626        name: &str,
627    ) -> Result<T, Error>
628    where
629        T: serde::de::DeserializeOwned,
630    {
631        let result = self.inner.get_value_ref(module_context, name)?;
632        self.inner.decode_value(result)
633    }
634
635    /// Executes the given module, and returns a handle allowing you to extract values
636    /// And call functions
637    ///
638    /// Blocks until the module has been executed AND the event loop has fully resolved
639    /// See [`crate::Runtime::load_module_async`] for a non-blocking variant, or use with async
640    /// background tasks
641    ///
642    /// # Arguments
643    /// * `module` - A `Module` object containing the module's filename and contents.
644    ///
645    /// # Returns
646    /// A `Result` containing a handle for the loaded module
647    /// or an error (`Error`) if there are issues with loading or executing the module
648    ///
649    /// # Errors
650    /// Can fail if the module cannot be loaded, or execution fails
651    ///
652    /// # Example
653    ///
654    /// ```rust
655    /// // Create a module with filename and contents
656    /// use rustyscript::{Runtime, Module, Error};
657    ///
658    /// # fn main() -> Result<(), Error> {
659    /// let mut runtime = Runtime::new(Default::default())?;
660    /// let module = Module::new("test.js", "export default () => 'test'");
661    /// runtime.load_module(&module);
662    /// # Ok(())
663    /// # }
664    /// ```
665    pub fn load_module(&mut self, module: &Module) -> Result<ModuleHandle, Error> {
666        self.block_on(|runtime| async move {
667            let handle = runtime.load_module_async(module).await;
668            runtime
669                .await_event_loop(PollEventLoopOptions::default(), None)
670                .await?;
671            handle
672        })
673    }
674
675    /// Executes the given module, and returns a handle allowing you to extract values
676    /// And call functions
677    ///
678    /// Returns a future that resolves to the handle for the loaded module
679    /// Makes no attempt to fully resolve the event loop - call [`crate::Runtime::await_event_loop`]
680    /// to resolve background tasks and async listeners
681    ///
682    /// # Arguments
683    /// * `module` - A `Module` object containing the module's filename and contents.
684    ///
685    /// # Returns
686    /// A `Result` containing a handle for the loaded module
687    /// or an error (`Error`) if there are issues with loading or executing the module
688    ///
689    /// # Errors
690    /// Can fail if the module cannot be loaded, or execution fails
691    ///
692    /// See [`crate::Runtime::load_module`] for an example
693    pub async fn load_module_async(&mut self, module: &Module) -> Result<ModuleHandle, Error> {
694        self.inner.load_modules(None, vec![module]).await
695    }
696
697    /// Executes the given module, and returns a handle allowing you to extract values
698    /// And call functions.
699    ///
700    /// Blocks until all modules have been executed AND the event loop has fully resolved
701    /// See [`crate::Runtime::load_module_async`] for a non-blocking variant, or use with async
702    /// background tasks
703    ///
704    /// This will load 'module' as the main module, and the others as side-modules.
705    /// Only one main module can be loaded per runtime
706    ///
707    /// # Arguments
708    /// * `module` - A `Module` object containing the module's filename and contents.
709    /// * `side_modules` - A set of additional modules to be loaded into memory for use
710    ///
711    /// # Returns
712    /// A `Result` containing a handle for the loaded module
713    /// or an error (`Error`) if there are issues with loading or executing the module
714    ///
715    /// # Errors
716    /// Can fail if the module cannot be loaded, or execution fails
717    ///
718    /// # Example
719    ///
720    /// ```rust
721    /// // Create a module with filename and contents
722    /// use rustyscript::{Runtime, Module, Error};
723    ///
724    /// # fn main() -> Result<(), Error> {
725    /// let mut runtime = Runtime::new(Default::default())?;
726    /// let module = Module::new("test.js", "export default () => 'test'");
727    /// runtime.load_modules(&module, vec![]);
728    /// # Ok(())
729    /// # }
730    /// ```
731    pub fn load_modules(
732        &mut self,
733        module: &Module,
734        side_modules: Vec<&Module>,
735    ) -> Result<ModuleHandle, Error> {
736        self.block_on(move |runtime| async move {
737            let handle = runtime.load_modules_async(module, side_modules).await;
738            runtime
739                .await_event_loop(PollEventLoopOptions::default(), None)
740                .await?;
741            handle
742        })
743    }
744
745    /// Executes the given module, and returns a handle allowing you to extract values
746    /// And call functions.
747    ///
748    /// Returns a future that resolves to the handle for the loaded module
749    /// Makes no attempt to resolve the event loop - call [`crate::Runtime::await_event_loop`] to
750    /// resolve background tasks and async listeners
751    ///
752    /// This will load 'module' as the main module, and the others as side-modules.
753    /// Only one main module can be loaded per runtime
754    ///
755    /// See [`crate::Runtime::load_modules`] for an example
756    ///
757    /// # Arguments
758    /// * `module` - A `Module` object containing the module's filename and contents.
759    /// * `side_modules` - A set of additional modules to be loaded into memory for use
760    ///
761    /// # Returns
762    /// A `Result` containing a handle for the loaded main module, or the last side-module
763    /// or an error (`Error`) if there are issues with loading or executing the modules
764    ///
765    /// # Errors
766    /// Can fail if the modules cannot be loaded, or execution fails
767    pub async fn load_modules_async(
768        &mut self,
769        module: &Module,
770        side_modules: Vec<&Module>,
771    ) -> Result<ModuleHandle, Error> {
772        self.inner.load_modules(Some(module), side_modules).await
773    }
774
775    /// Executes the given module, on the runtime, making it available to be
776    /// imported by other modules in this runtime, and those that will use the
777    /// snapshot
778    ///
779    /// This is a blocking operation, and will run the event loop to completion
780    /// For a non-blocking variant, see [`SnapshotBuilder::load_module_async`]
781    ///
782    /// # Arguments
783    /// * `module` - A `Module` object containing the module's filename and contents.
784    ///
785    /// # Errors
786    /// Can fail if the module cannot be loaded, or execution fails
787    pub fn with_module(mut self, module: &Module) -> Result<Self, Error> {
788        self.load_module(module)?;
789        Ok(self)
790    }
791
792    /// Executes a piece of non-ECMAScript-module JavaScript code on the runtime
793    /// This code can be used to set up the runtime state before creating the snapshot
794    ///
795    /// This is a blocking operation, and will run the event loop to completion
796    ///
797    /// # Arguments
798    /// * `expr` - A string representing the JavaScript expression to evaluate
799    ///
800    /// # Errors
801    /// Can fail if the expression cannot be evaluated, or if the result cannot be deserialized
802    pub fn with_expression(mut self, expr: &str) -> Result<Self, Error> {
803        self.eval::<()>(expr)?;
804        Ok(self)
805    }
806
807    /// Consumes the runtime and returns a snapshot of the runtime state
808    /// This is only available when the `snapshot_builder` feature is enabled
809    /// and will return a `Box<[u8]>` representing the snapshot
810    ///
811    /// To use the snapshot, provide it, as a static slice, in [`RuntimeOptions::startup_snapshot`]
812    /// Therefore, in order to use this snapshot, make sure you write it to a file and load it with
813    /// `include_bytes!`
814    ///
815    /// WARNING: In order to use the snapshot, make sure the runtime using it is
816    /// provided the same extensions and options as the original runtime.
817    #[must_use]
818    pub fn finish(self) -> Box<[u8]> {
819        let deno_rt: JsRuntimeForSnapshot = self.inner.into_inner();
820        deno_rt.snapshot()
821    }
822}
823
824impl AsyncBridgeExt for SnapshotBuilder {
825    fn bridge(&self) -> &AsyncBridge {
826        &self.tokio
827    }
828}