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