1#![cfg_attr(docsrs, feature(doc_cfg))]
2#![doc = include_str!("../README.md")]
3
4use std::{error::Error, fmt::Display, sync::Arc};
5
6#[cfg(feature = "compile")]
7mod compile;
8#[cfg(feature = "compile")]
9pub use compile::*;
10
11pub use dioxus_use_js_macro::use_js;
12
13#[doc(hidden)]
16pub use serde::Serialize as SerdeSerialize;
17#[doc(hidden)]
18pub use serde::de::Error as SerdeDeError;
19#[doc(hidden)]
20pub use serde::de::DeserializeOwned as SerdeDeDeserializeOwned;
21#[doc(hidden)]
22pub use serde_json::Value as SerdeJsonValue;
23#[doc(hidden)]
24pub use serde_json::from_value as serde_json_from_value;
25#[doc(hidden)]
26pub use serde_json::Error as SerdeJsonError;
27#[doc(hidden)]
28pub const __SEND_VALIDATION_MSG: &str = "Should always send back a value that is an array of two.";
29#[doc(hidden)]
30pub const __INDEX_VALIDATION_MSG: &str = "The first sent back value should always be a u64.";
31#[doc(hidden)]
32pub const __BAD_CALL_MSG: &str = "Should only attempt to call known actions.";
33#[doc(hidden)]
34pub const __BAD_VOID_RETURN: &str = "A function that should return no value instead returned a value";
35fn _send_sync_error_assert() {
43 fn is_send<T: Send>(_: &T) {}
44 fn is_sync<T: Sync>(_: &T) {}
45 fn is_error<T: Error>(_: &T) {}
46
47 let o: JsError =
48 JsError::Callback(Box::new(std::io::Error::new(std::io::ErrorKind::Other, ""))
49 as Box<dyn Error + Send + Sync>);
50 is_send(&o);
51 is_sync(&o);
52 is_error(&o);
53}
54
55#[derive(Debug)]
57pub enum JsError {
58 Eval(dioxus::document::EvalError),
60 Callback(Box<dyn Error + Send + Sync>),
62}
63
64impl std::fmt::Display for JsError {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 match self {
67 JsError::Eval(e) => write!(f, "JavaScript error: {}", e),
68 JsError::Callback(error) => write!(f, "Callback error: {}", error),
69 }
70 }
71}
72
73impl std::error::Error for JsError {}
74
75#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
92pub struct JsValue(Arc<Inner>);
93
94#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
96struct Inner(String);
97
98impl JsValue {
99 #[deprecated(note = "This constructor is for internal use only. Do not use directly.")]
100 #[doc(hidden)]
101 pub fn internal_create(id: String) -> Self {
102 Self(Arc::new(Inner(id)))
103 }
104
105 #[deprecated(note = "This is for internal use only. Do not use directly.")]
106 #[doc(hidden)]
107 pub fn internal_get(&self) -> &str {
108 self.0.0.as_str()
109 }
110}
111
112impl Display for JsValue {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 write!(f, "Value in js: window[\"{}\"]", self.0.0)
115 }
116}
117
118impl Drop for Inner {
119 fn drop(&mut self) {
120 let object_name = std::mem::take(&mut self.0);
121 dioxus::core::spawn_forever(async move {
123 let eval = dioxus::document::eval(
124 r#"
125const objectName = await dioxus.recv();
126if (window.hasOwnProperty(objectName)) {
127 delete window[objectName];
128}
129return null;
130"#,
131 );
132 if let Err(error) = eval.send(object_name.as_str()) {
133 dioxus::logger::tracing::error!(
134 "Failed to send object name to clean up `window[\"{object_name}\"]`. Error: {error}"
135 );
136 }
137 if let Err(error) = eval.await {
138 dioxus::logger::tracing::error!(
139 "Failed to clean up JavaScript object `window[\"{object_name}\"]`. Error: {error}"
140 );
141 } else {
142 dioxus::logger::tracing::trace!(
143 "Successfully dropped JsValue and cleaned up JavaScript object `window[\"{object_name}\"]`."
144 );
145 }
146 });
147 }
148}