libp2p_webtransport_websys/
utils.rs

1use std::{io, sync::LazyLock};
2
3use js_sys::{Promise, Reflect};
4use send_wrapper::SendWrapper;
5use wasm_bindgen::{JsCast, JsValue};
6
7use crate::Error;
8
9type Closure = wasm_bindgen::closure::Closure<dyn FnMut(JsValue)>;
10static DO_NOTHING: LazyLock<SendWrapper<Closure>> = LazyLock::new(|| {
11    let cb = Closure::new(|_| {});
12    SendWrapper::new(cb)
13});
14
15/// Properly detach a promise.
16///
17/// A promise always runs in the background, however if you don't await it,
18/// or specify a `catch` handler before you drop it, it might cause some side
19/// effects. This function avoids any side effects.
20// Ref: https://github.com/typescript-eslint/typescript-eslint/blob/391a6702c0a9b5b3874a7a27047f2a721f090fb6/packages/eslint-plugin/docs/rules/no-floating-promises.md
21pub(crate) fn detach_promise(promise: Promise) {
22    // Avoid having "floating" promise and ignore any errors.
23    // After `catch` promise is allowed to be dropped.
24    let _ = promise.catch(&DO_NOTHING);
25}
26
27/// Typecasts a JavaScript type.
28///
29/// Returns a `Ok(value)` casted to the requested type.
30///
31/// If the underlying value is an error and the requested
32/// type is not, then `Err(Error::JsError)` is returned.
33///
34/// If the underlying value can not be casted to the requested type and
35/// is not an error, then `Err(Error::JsCastFailed)` is returned.
36pub(crate) fn to_js_type<T>(value: impl Into<JsValue>) -> Result<T, Error>
37where
38    T: JsCast + From<JsValue>,
39{
40    let value = value.into();
41
42    if value.has_type::<T>() {
43        Ok(value.unchecked_into())
44    } else if value.has_type::<js_sys::Error>() {
45        Err(Error::from_js_value(value))
46    } else {
47        Err(Error::JsCastFailed)
48    }
49}
50
51/// Parse response from `ReadableStreamDefaultReader::read`.
52// Ref: https://streams.spec.whatwg.org/#default-reader-prototype
53pub(crate) fn parse_reader_response(resp: &JsValue) -> Result<Option<JsValue>, JsValue> {
54    let value = Reflect::get(resp, &JsValue::from_str("value"))?;
55    let done = Reflect::get(resp, &JsValue::from_str("done"))?
56        .as_bool()
57        .unwrap_or_default();
58
59    if value.is_undefined() || done {
60        Ok(None)
61    } else {
62        Ok(Some(value))
63    }
64}
65
66pub(crate) fn to_io_error(value: JsValue) -> io::Error {
67    io::Error::new(io::ErrorKind::Other, Error::from_js_value(value))
68}