1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3
4use std::ops::{Deref, DerefMut};
5use std::pin::Pin;
6use std::task::{Context, Poll};
7use std::{error::Error, fmt::Display, sync::Arc};
8
9#[cfg(feature = "build")]
10mod build;
11#[cfg(feature = "build")]
12pub use build::*;
13
14use dioxus::document::{self, Eval};
15pub use dioxus_use_js_macro::use_js;
16
17#[doc(hidden)]
20pub use serde::Serialize as SerdeSerialize;
21#[doc(hidden)]
22pub use serde::de::DeserializeOwned as SerdeDeDeserializeOwned;
23#[doc(hidden)]
24pub use serde::de::Error as SerdeDeError;
25#[doc(hidden)]
26pub use serde_json::Error as SerdeJsonError;
27#[doc(hidden)]
28pub use serde_json::Value as SerdeJsonValue;
29#[doc(hidden)]
30pub use serde_json::from_value as serde_json_from_value;
31#[doc(hidden)]
32pub const __CALLBACK_SEND_VALIDATION_MSG: &str =
33 "Callbacks should always send back a value that is an array of three.";
34#[doc(hidden)]
35pub const __RESULT_SEND_VALIDATION_MSG: &str =
36 "Result should always send back a value that is an array of two.";
37#[doc(hidden)]
38pub const __INDEX_VALIDATION_MSG: &str = "The index value was an unexpected type";
39#[doc(hidden)]
40pub const __BAD_CALL_MSG: &str = "Should only attempt to call known actions.";
41#[doc(hidden)]
42pub const __BAD_VOID_RETURN: &str =
43 "A function that should return no value instead returned a value";
44#[doc(hidden)]
45pub const __UNEXPECTED_CALLBACK_TYPE: &str = "The callback was called with the wrong type";
46fn _send_sync_error_assert() {
54 fn is_send<T: Send>(_: &T) {}
55 fn is_sync<T: Sync>(_: &T) {}
56 fn is_error<T: Error>(_: &T) {}
57
58 let o: JsError = JsError::Threw { func: "" };
59 is_send(&o);
60 is_sync(&o);
61 is_error(&o);
62}
63
64#[derive(Debug)]
66pub enum JsError {
67 Eval {
71 func: &'static str,
73 error: dioxus::document::EvalError,
74 },
75 Threw {
77 func: &'static str,
79 },
80}
81
82impl std::fmt::Display for JsError {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 match self {
85 JsError::Eval { func: name, error } => {
86 write!(f, "JavaScript function '{}' eval error: {}", name, error)
87 }
88 JsError::Threw { func: name } => {
89 write!(
90 f,
91 "JavaScript function '{}' threw an error during execution",
92 name
93 )
94 }
95 }
96 }
97}
98
99impl std::error::Error for JsError {}
100
101#[derive(
118 serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash,
119)]
120pub struct JsValue(Arc<JsValueInner>);
121
122#[derive(
124 serde::Serialize, serde::Deserialize, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash,
125)]
126struct JsValueInner(String);
127
128impl JsValue {
129 #[deprecated(note = "This constructor is for internal use only. Do not use directly.")]
130 #[doc(hidden)]
131 pub fn internal_create(id: String) -> Self {
132 Self(Arc::new(JsValueInner(id)))
133 }
134
135 #[deprecated(note = "This is for internal use only. Do not use directly.")]
136 #[doc(hidden)]
137 pub fn internal_get(&self) -> &str {
138 self.0.0.as_str()
139 }
140}
141
142impl Display for JsValue {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 write!(f, "Value in js: window[\"{}\"]", self.0.0)
145 }
146}
147
148impl Drop for JsValueInner {
149 fn drop(&mut self) {
150 let object_name = std::mem::take(&mut self.0);
151 dioxus::core::spawn_forever(async move {
152 let eval =
153 dioxus::document::eval(&format!("delete window[\"{object_name}\"];return null;"));
154 if let Err(error) = eval.await {
155 dioxus::logger::tracing::error!(
156 "Failed to clean up JavaScript object `window[\"{object_name}\"]`. Error: {error}"
157 );
158 } else {
159 dioxus::logger::tracing::trace!(
160 "Cleaned up JavaScript object `window[\"{object_name}\"]`."
161 );
162 }
163 });
164 }
165}
166
167#[doc(hidden)]
177pub struct SignalDrop(String);
178
179impl SignalDrop {
180 pub fn new(invocation_id: String) -> Self {
181 SignalDrop(invocation_id)
182 }
183}
184
185impl Drop for SignalDrop {
186 fn drop(&mut self) {
187 let invocation_id = std::mem::take(&mut self.0);
188 dioxus::core::spawn_forever(async move {
189 let eval =
191 dioxus::document::eval(&format!("let i=\"{invocation_id}d\";let p=window[i];delete window[i];p();dioxus.close();return null;"));
192 if let Err(error) = eval.await {
193 dioxus::logger::tracing::error!(
194 "Failed to notify of drop for invocation `window[\"{invocation_id}d\"]`. Error: {error}"
195 );
196 } else {
197 dioxus::logger::tracing::trace!(
198 "Notified of drop for invocation `window[\"{invocation_id}d\"]`."
199 );
200 }
201 });
202 }
203}
204
205#[doc(hidden)]
207#[derive(Clone)]
208pub struct CallbackResponder(Arc<CallbackResponderInner>);
209
210struct CallbackResponderInner(Eval);
211
212impl CallbackResponder {
213 pub fn new(invocation_id: &str) -> Self {
214 CallbackResponder(Arc::new(CallbackResponderInner(dioxus::document::eval(
216 &format!(
217 "while(true){{let r=await dioxus.recv();if(!Array.isArray(r))break;let f=window[\"{invocation_id}\"];if(f==null)break;let i=r[0],o=r[1],d=r[2],x=f[i];delete f[i];if(o)x[0](d);else x[1](d);}}",
218 ),
219 ))))
220 }
221
222 pub fn respond<T: serde::Serialize>(&self, request_id: u64, is_ok: bool, data: T) {
223 let payload = (request_id, is_ok, data);
224
225 let result = self.0.0.send(payload);
226 if let Err(e) = result {
227 dioxus::logger::tracing::error!(
228 "Failed to send callback response for invocation '{}' request '{}': {}",
229 request_id,
230 is_ok,
231 e
232 );
233 }
234 }
235}
236
237impl Drop for CallbackResponderInner {
238 fn drop(&mut self) {
239 let result = self.0.send(serde_json::Value::Null);
241 if let Err(e) = result {
242 dioxus::logger::tracing::error!("Failed to shut down callback responder: {}", e);
243 }
244 }
245}
246
247#[doc(hidden)]
248pub struct PendingFuture;
249
250impl Future for PendingFuture {
251 type Output = ();
252
253 fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
254 Poll::Pending
255 }
256}
257
258