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 {
153 let eval =
154 dioxus::document::eval(&format!("delete window[\"{object_name}\"];return null;"));
155 if let Err(error) = eval.await {
156 dioxus::logger::tracing::error!(
157 "Failed to clean up JavaScript object `window[\"{object_name}\"]`. Error: {error}"
158 );
159 } else {
160 dioxus::logger::tracing::trace!(
161 "Cleaned up JavaScript object `window[\"{object_name}\"]`."
162 );
163 }
164 });
165 }
166}
167
168#[doc(hidden)]
172pub struct EvalDrop(document::Eval);
173
174impl EvalDrop {
175 pub fn new(eval: document::Eval) -> Self {
177 Self(eval)
178 }
179}
180
181impl Deref for EvalDrop {
182 type Target = document::Eval;
183
184 fn deref(&self) -> &Self::Target {
185 &self.0
186 }
187}
188
189impl DerefMut for EvalDrop {
190 fn deref_mut(&mut self) -> &mut Self::Target {
191 &mut self.0
192 }
193}
194
195impl Drop for EvalDrop {
196 fn drop(&mut self) {
197 if let Err(e) = self.0.send(serde_json::Value::Null) {
198 dioxus::logger::tracing::error!("Failed to notify about Eval drop: {}", e);
199 }
200 }
201}
202
203#[doc(hidden)]
205#[derive(Clone)]
206pub struct CallbackResponder(Arc<CallbackResponderInner>);
207
208struct CallbackResponderInner(Eval);
209
210impl CallbackResponder {
211 pub fn new(invocation_id: &str) -> Self {
212 CallbackResponder(Arc::new(CallbackResponderInner(dioxus::document::eval(
214 &format!(
215 "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);}}",
216 ),
217 ))))
218 }
219
220 pub fn respond<T: serde::Serialize>(&self, request_id: u64, is_ok: bool, data: T) {
221 let payload = (request_id, is_ok, data);
222
223 let result = self.0.0.send(payload);
224 if let Err(e) = result {
225 dioxus::logger::tracing::error!(
226 "Failed to send callback response for invocation '{}' request '{}': {}",
227 request_id,
228 is_ok,
229 e
230 );
231 }
232 }
233}
234
235impl Drop for CallbackResponderInner {
236 fn drop(&mut self) {
237 let result = self.0.send(serde_json::Value::Null);
239 if let Err(e) = result {
240 dioxus::logger::tracing::error!("Failed to shut down callback responder: {}", e);
241 }
242 }
243}
244
245#[doc(hidden)]
246pub struct PendingFuture;
247
248impl Future for PendingFuture {
249 type Output = ();
250
251 fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> {
252 Poll::Pending
253 }
254}
255
256