1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
use crate::codec;
use crate::workers::web_worker::WebWorker;
use futures::future::BoxFuture;
use std::cell::RefCell;
use std::sync::Mutex;
use wasm_bindgen::prelude::*;

pub trait FutureWorker: WebWorker {
    fn run(request: Self::Request) -> BoxFuture<'static, Self::Response>;
}

#[wasm_bindgen]
#[derive(Clone)]
pub struct FutureWorkerFn {
    pub(crate) path: &'static str,
    pub(crate) function: fn(&Vec<u8>) -> BoxFuture<'static, JsValue>,
}

impl FutureWorkerFn {
    #[must_use]
    pub fn new<W: FutureWorker>() -> Self {
        Self {
            path: W::path(),
            function: move |request| {
                let request = codec::from_slice(&request[..]).expect("byte deserialization error");
                Box::pin(async move {
                    let response = W::run(request).await;
                    serde_wasm_bindgen::to_value(&response).expect("js serialization error")
                })
            },
        }
    }
}

mod private {
    use crate::workers::future_worker::{FutureWorkerFn, FUTURE_WORKER_FN};
    use js_sys::global;
    use wasm_bindgen::prelude::wasm_bindgen;
    use wasm_bindgen::JsValue;
    use web_sys::DedicatedWorkerGlobalScope;

    #[wasm_bindgen]
    pub fn register_future_worker(future_worker: &FutureWorkerFn) {
        console_error_panic_hook::set_once();

        let worker_scope: DedicatedWorkerGlobalScope = JsValue::from(global()).into();
        if worker_scope.name() == future_worker.path {
            let cell = FUTURE_WORKER_FN
                .lock()
                .expect("failed to lock FUTURE_WORKER_FN");
            let mut opt = cell.borrow_mut();
            *opt = Some(future_worker.clone());
        }
    }
}

pub(crate) static FUTURE_WORKER_FN: Mutex<RefCell<Option<FutureWorkerFn>>> =
    Mutex::new(RefCell::new(None));