1use js_sys::{Array, Object};
2use wasm_bindgen::JsValue;
3use web_sys::{Blob, BlobPropertyBag, Url, Worker};
4
5pub fn worker_from_str(worker_str: &str) -> Result<Worker, JsValue> {
6 let blob_parts = str_array_to_js_value(&[worker_str]);
7 let blob = Blob::new_with_str_sequence_and_options(
8 &blob_parts,
9 BlobPropertyBag::new().type_("application/javascript"),
10 )?;
11 let url_obj = Url::create_object_url_with_blob(&blob)?;
12 Worker::new(&url_obj)
13}
14
15pub fn worker_from_runner(runner_name: &str, pkg_name: &str) -> Result<Worker, JsValue> {
16 let pkg_name = pkg_name.to_lowercase().replace('-', "_");
17 let worker_str = format!(
18 "
19 self.onmessage = (event) => {{
20 // The web-worker lives in a different prefixed namespace than the main
21 // thread. To make scripts from the main thread available, it needs to send
22 // its URL, which we receive here.
23 importScripts(event.data.url + '/pkg/{pkg_name}.js')
24 console.log('Imported script in worker_from_runner')
25
26 const {{ {runner_name} }} = wasm_bindgen
27
28 console.log('Launching another runner')
29
30 async function run_in_worker() {{
31 // Load the wasm file by awaiting the Promise returned by `wasm_bindgen`.
32 await wasm_bindgen(event.data.url + '/pkg/{pkg_name}_bg.wasm')
33
34 const runner = {runner_name}.new()
35 runner.init()
36
37 self.onmessage = async (event) => {{
38 console.log('JS: Receive event')
39 runner.onmessage(event)
40 }}
41 }}
42
43 run_in_worker()
44 }}
45 "
46 );
47 let worker = worker_from_str(&worker_str)?;
48 let site_url = get_url()?;
49 let obj = create_obj(vec![("url", JsValue::from_str(&site_url))]);
50 worker.post_message(&obj)?;
51 Ok(worker)
52}
53
54pub fn get_url() -> Result<String, JsValue> {
55 let document = web_sys::window().unwrap().document().unwrap();
56 let location = document.location().unwrap();
57
58 let protocol = location.protocol()?;
59 let host = location.host()?;
60
61 Ok(format!("{protocol}//{host}"))
62}
63
64fn str_array_to_js_value(array: &[&str]) -> JsValue {
65 JsValue::from(
66 array
67 .iter()
68 .map(|&x| JsValue::from(x))
69 .collect::<js_sys::Array>(),
70 )
71}
72
73fn create_obj(iterable: Vec<(&str, JsValue)>) -> Object {
74 let props = iterable
75 .into_iter()
76 .map(|(name, value)| [JsValue::from_str(name), value].iter().collect::<Array>())
77 .collect::<Array>();
78 js_sys::Object::from_entries(&JsValue::from(props)).unwrap()
79}