web_message/
message.rs

1use js_sys::Array;
2use js_sys::wasm_bindgen;
3use wasm_bindgen::{JsCast, JsValue};
4use web_sys::js_sys;
5
6use crate::Error;
7pub trait Message: Sized {
8	// Serializes the message into a JsValue.
9	// Any transferable fields are appended to the given array.
10	fn into_message(self, transferable: &mut Array) -> JsValue;
11
12	// Deserializes the message from a JsValue.
13	fn from_message(message: JsValue) -> Result<Self, Error>;
14}
15
16macro_rules! upstream {
17	($($t:ty),*) => {
18		$(
19			impl Message for $t {
20				fn into_message(self, _transferable: &mut Array) -> JsValue {
21					self.into()
22				}
23
24				fn from_message(message: JsValue) -> Result<Self, Error> {
25					Self::try_from(message).map_err(|_| Error::UnexpectedType)
26				}
27			}
28		)*
29	};
30}
31
32// Macro for implementing Message for primitive casts supported in wasm-bindgen
33upstream!(String, f64, i128, i64, u128, u64);
34
35macro_rules! integer {
36	($($t:ty),*) => {
37		$(
38			impl Message for $t {
39				fn into_message(self, _transferable: &mut Array) -> JsValue {
40					self.into()
41				}
42
43				fn from_message(message: JsValue) -> Result<Self, Error> {
44					Ok(message.as_f64().ok_or(Error::UnexpectedType)? as $t)
45				}
46			}
47		)*
48	};
49}
50
51// Macro for implementing Message for floating point types, less than Number.MAX_SAFE_INTEGER
52integer!(u32, i32, u16, i16, u8, i8);
53
54impl Message for bool {
55	fn into_message(self, _transferable: &mut Array) -> JsValue {
56		self.into()
57	}
58
59	fn from_message(message: JsValue) -> Result<Self, Error> {
60		message.as_bool().ok_or(Error::UnexpectedType)
61	}
62}
63
64impl<T: Message> Message for Option<T> {
65	fn into_message(self, transferable: &mut Array) -> JsValue {
66		match self {
67			Some(value) => value.into_message(transferable),
68			None => JsValue::NULL,
69		}
70	}
71
72	fn from_message(message: JsValue) -> Result<Self, Error> {
73		Ok(match message.is_null() {
74			true => None,
75			false => Some(T::from_message(message)?),
76		})
77	}
78}
79
80impl<T: Message> Message for Vec<T> {
81	fn into_message(self, transferable: &mut Array) -> JsValue {
82		let array = Array::new();
83		for value in self {
84			array.push(&value.into_message(transferable));
85		}
86		array.into()
87	}
88
89	fn from_message(message: JsValue) -> Result<Self, Error> {
90		if !message.is_array() {
91			return Err(Error::UnexpectedType);
92		}
93
94		let array = Array::from(&message);
95		let mut values = Vec::with_capacity(array.length() as usize);
96		for i in 0..array.length() {
97			values.push(T::from_message(array.get(i))?);
98		}
99		Ok(values)
100	}
101}
102
103impl Message for js_sys::ArrayBuffer {
104	fn into_message(self, transferable: &mut Array) -> JsValue {
105		transferable.push(&self);
106		self.into()
107	}
108
109	fn from_message(message: JsValue) -> Result<Self, Error> {
110		message
111			.dyn_into::<js_sys::ArrayBuffer>()
112			.map_err(|_| Error::UnexpectedType)
113	}
114}
115
116macro_rules! typed_array {
117	($($t:ident),*,) => {
118		$(
119			impl Message for js_sys::$t {
120				fn into_message(self, transferable: &mut Array) -> JsValue {
121					transferable.push(&self.buffer());
122					self.into()
123				}
124
125				fn from_message(message: JsValue) -> Result<Self, Error> {
126					message
127						.dyn_into::<js_sys::$t>()
128						.map_err(|_| Error::UnexpectedType)
129				}
130			}
131		)*
132	};
133}
134
135// These are all the types that wrap an ArrayBuffer and can be transferred.
136typed_array!(
137	Float32Array,
138	Float64Array,
139	Int8Array,
140	Int16Array,
141	Int32Array,
142	Uint8Array,
143	Uint8ClampedArray,
144	Uint16Array,
145	Uint32Array,
146	BigInt64Array,
147	BigUint64Array,
148);
149
150macro_rules! transferable_feature {
151	($($feature:literal = $t:ident),* $(,)?) => {
152		$(
153			#[cfg(feature = $feature)]
154			impl Message for web_sys::$t {
155				fn into_message(self, transferable: &mut Array) -> JsValue {
156					transferable.push(&self);
157					self.into()
158				}
159
160				fn from_message(message: JsValue) -> Result<Self, Error> {
161					message
162						.dyn_into::<web_sys::$t>()
163						.map_err(|_| Error::UnexpectedType)
164				}
165			}
166		)*
167	};
168}
169
170// These feature names copy web_sys for all (currently) transferable types.
171// https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects
172transferable_feature!(
173	"MessagePort" = MessagePort,
174	"ReadableStream" = ReadableStream,
175	"WritableStream" = WritableStream,
176	"TransformStream" = TransformStream,
177	"WebTransportReceiveStream" = WebTransportReceiveStream,
178	"WebTransportSendStream" = WebTransportSendStream,
179	"AudioData" = AudioData,
180	"ImageBitmap" = ImageBitmap,
181	"VideoFrame" = VideoFrame,
182	"OffscreenCanvas" = OffscreenCanvas,
183	"RtcDataChannel" = RtcDataChannel,
184	//"MediaSourceHandle" = MediaSourceHandle,
185	"MidiAccess" = MidiAccess,
186);
187
188#[cfg(feature = "Url")]
189impl Message for url::Url {
190	fn into_message(self, _transferable: &mut Array) -> JsValue {
191		self.to_string().into()
192	}
193
194	fn from_message(message: JsValue) -> Result<Self, Error> {
195		let str = message.as_string().ok_or(Error::UnexpectedType)?;
196		url::Url::parse(&str).map_err(Error::InvalidUrl)
197	}
198}