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 fn into_message(self, transferable: &mut Array) -> JsValue;
11
12 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
32upstream!(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
51integer!(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
135typed_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
170transferable_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 "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}