dioxus_use_js/
lib.rs

1#![doc = include_str!("../README.md")]
2
3use std::{fmt::Display, sync::Arc};
4
5pub use dioxus_use_js_macro::use_js;
6
7// We export these so downstreams don't need `serde` or `serde_json` directly
8pub use serde_json::Error as SerdeJsonError;
9// exports used by macro.
10pub use serde::Serialize as SerdeSerialize;
11pub use serde_json::Value as SerdeJsonValue;
12
13#[doc(hidden)]
14pub use serde_json::from_value as serde_json_from_value;
15
16// We do not export this so the dioxus version doing the eval is the same, otherwise it may compile but using two different versions of dioxus at runtime will likely cause a runtime error
17// be two different versions of dioxus in the graph
18// pub use dioxus::document::eval as dioxus_document_eval;
19// pub use dioxus::document::EvalError as DioxusEvalError;
20
21
22
23//************************************************************************//
24
25/// An error related to the execution of a javascript operation
26#[derive(Debug)]
27pub enum JsError {
28    Eval(dioxus::document::EvalError),
29    Deserialize(SerdeJsonError),
30}
31
32impl std::fmt::Display for JsError {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        match self {
35            JsError::Eval(e) => write!(f, "JavaScript evaluation error: {}", e),
36            JsError::Deserialize(e) => write!(f, "Deserialization output error: {}", e),
37        }
38    }
39}
40
41impl std::error::Error for JsError {}
42
43//************************************************************************//
44
45/// A reference to a javascript value that can be held on the dioxus side and passed to functions generated
46/// by this crate.
47/// 
48/// An instance of this is created or used by e.g.
49/// ```rust,ignore
50/// dioxus_use_js::use_js! {
51///     ts: "ts/example.ts",
52///     bundle: "assets/example.js",
53///     functions: usingJsValue,
54/// }
55/// ```
56/// Where `"ts/example.ts"` uses this marker type
57/// ```ts
58/// type JsValue<T = any> = T;
59/// ```
60/// And `usingJsValue` uses `JsValue`.
61/// 
62/// This uses `Arc` internally and the value on the js side is destroyed when the last reference is dropped
63// Dev Note: No `serde::Serialize` or `serde::Deserialize` on purpose since the value is destroyed when dropped
64#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
65pub struct JsValue(Arc<Inner>);
66
67/// Abstraction used to implement the one time drop
68#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
69struct Inner(String);
70
71impl JsValue {
72    #[deprecated(note = "This constructor is for internal use only. Do not use directly.")]
73    #[doc(hidden)]
74    pub fn internal_create(id: String) -> Self {
75        Self(Arc::new(Inner(id)))
76    }
77
78    #[deprecated(note = "This is for internal use only. Do not use directly.")]
79    #[doc(hidden)]
80    pub fn internal_get(&self) -> &str {
81        self.0.0.as_str()
82    }
83}
84
85impl Display for JsValue {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        write!(f, "Value in js: window[\"{}\"]", self.0.0)
88    }
89}
90
91impl Drop for Inner {
92    fn drop(&mut self) {
93        let object_name = std::mem::take(&mut self.0);
94        // work around for no async drop trait
95        dioxus::core::spawn_forever(async move {
96            let eval = dioxus::document::eval(
97                r#"
98const objectName = await dioxus.recv();
99if (window.hasOwnProperty(objectName)) {
100    delete window[objectName];
101}
102return null;
103"#,
104            );
105            if let Err(error) = eval.send(object_name.as_str()) {
106                dioxus::logger::tracing::error!(
107                    "Failed to send object name to clean up `window[\"{object_name}\"]`. Error: {error}"
108                );
109            }
110            if let Err(error) = eval.await {
111                dioxus::logger::tracing::error!(
112                    "Failed to clean up JavaScript object `window[\"{object_name}\"]`. Error: {error}"
113                );
114            } else {
115                dioxus::logger::tracing::trace!(
116                    "Successfully dropped JsValue and cleaned up JavaScript object `window[\"{object_name}\"]`."
117                );
118            }
119        });
120    }
121}