web_thread/post.rs
1use super::{JsValue, js_sys};
2
3/// Objects that can be sent via `postMessage`. A type that is `Post`
4/// supports being serialized into a JavaScript object that can be
5/// sent using `postMessage`, and also getting an array of subobjects
6/// that must be
7/// [transferred](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects).
8pub trait Post: AsJs {
9 /// Get a list of the objects that must be
10 /// transferred when calling `postMessage`.
11 ///
12 /// The default implementation returns an empty array.
13 fn transferables(&self) -> js_sys::Array {
14 js_sys::Array::new()
15 }
16}
17
18/// Convenience trait for something that can have messages posted to
19/// it, including
20/// [transferables](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects).
21pub trait PostExt {
22 /// Send a value, transferring subobjects as necessary.
23 ///
24 /// This function consumes `message`, as in general it may leave
25 /// the object in an incoherent state.
26 ///
27 /// # Errors
28 ///
29 /// If the message could not be sent.
30 fn post(&self, message: impl Post) -> Result<(), JsValue>;
31}
32
33impl PostExt for web_sys::MessagePort {
34 fn post(&self, message: impl Post) -> Result<(), JsValue> {
35 // While not syntactically consumed, the use of `postMessage`
36 // here may leave `Context` in an invalid state (setting
37 // transferred JavaScript values to `undefined`).
38 #![allow(clippy::needless_pass_by_value)]
39
40 self.post_message_with_transferable(&message.to_js()?, &message.transferables())
41 }
42}
43
44impl PostExt for web_sys::Worker {
45 fn post(&self, message: impl Post) -> Result<(), JsValue> {
46 // While not syntactically consumed, the use of `postMessage`
47 // here may leave `Context` in an invalid state (setting
48 // transferred JavaScript values to `undefined`).
49 #![allow(clippy::needless_pass_by_value)]
50
51 self.post_message_with_transfer(&message.to_js()?, &message.transferables())
52 }
53}
54
55/// A serializable (JS-friendly) representation of a message plus its
56/// transferables.
57#[derive(serde::Serialize)]
58pub struct Postable {
59 #[serde(with = "serde_wasm_bindgen::preserve")]
60 message: JsValue,
61 #[serde(with = "serde_wasm_bindgen::preserve")]
62 transfer: js_sys::Array,
63}
64
65impl Postable {
66 pub fn new(message: impl Post) -> Result<Self, JsValue> {
67 // While not syntactically consumed, the use of `postMessage`
68 // may leave `Context` in an invalid state (setting
69 // transferred JavaScript values to `undefined`).
70 #![allow(clippy::needless_pass_by_value)]
71
72 Ok(Self {
73 message: message.to_js()?,
74 transfer: message.transferables(),
75 })
76 }
77}
78
79/// An object-safe version of
80/// `std::convert::TryInto`/`std::convert::TryFrom`, relying on the
81/// JavaScript GC.
82pub trait AsJs {
83 /// Retrieve the JavaScript representation of a value.
84 ///
85 /// # Errors
86 ///
87 /// If the value could not be represented as a JavaScript value.
88 fn to_js(&self) -> Result<JsValue, JsValue>;
89 /// Construct the value from its JavaScript representation.
90 /// Unlike [`to_js`] this function consumes its argument, as the
91 /// value or its subvalues may be transferred into the resulting
92 /// Rust object.
93 ///
94 /// # Errors
95 ///
96 /// If the value could not be reconstructed from the provided
97 /// JavaScript value.
98 ///
99 /// [`to_js`]: AsJs::to_js()
100 fn from_js(js_value: JsValue) -> Result<Self, JsValue>
101 where
102 Self: Sized;
103}
104
105impl<T: serde::Serialize + serde::de::DeserializeOwned> AsJs for T {
106 fn to_js(&self) -> Result<JsValue, JsValue> {
107 Ok(serde_wasm_bindgen::to_value(self)?)
108 }
109
110 fn from_js(value: JsValue) -> Result<Self, JsValue>
111 where
112 Self: Sized,
113 {
114 Ok(serde_wasm_bindgen::from_value(value)?)
115 }
116}
117
118impl Post for () {}
119impl Post for u8 {}
120impl Post for u16 {}
121impl Post for u32 {}
122impl Post for u64 {}
123impl Post for u128 {}
124impl Post for i8 {}
125impl Post for i16 {}
126impl Post for i32 {}
127impl Post for i64 {}
128impl Post for i128 {}
129impl Post for String {}
130
131impl<T: Post, E: Post> Post for Result<T, E>
132where
133 Result<T, E>: AsJs,
134{
135 fn transferables(&self) -> js_sys::Array {
136 match self {
137 Ok(x) => x.transferables(),
138 Err(e) => e.transferables(),
139 }
140 }
141}